iwmlib/lib/pixi/flipeffect.js

278 lines
9.0 KiB
JavaScript

import { getId, Angle } from '../utils.js'
import { DOMScatter } from '../scatter.js'
import { CardLoader, DOMFlip, DOMFlippable } from '../flippable.js'
import { Capabilities } from '../capabilities.js'
import { DeepZoomImage } from './deepzoom/image.js'
let globalScatterLoaderCanvas = null
export class ScatterLoader extends CardLoader {
get scatter() {
return this.src
}
unscaledSize() {
let displayObject = this.scatter.displayObject
let w = displayObject.width
let h = displayObject.height
return [w / displayObject.scale.x, h / displayObject.scale.y]
}
scaledSize() {
let displayObject = this.scatter.displayObject
let w = displayObject.width
let h = displayObject.height
return [w, h]
}
cloneScatterImage() {
let w = this.scatter.width
let h = this.scatter.height
let isSprite = this.scatter.displayObject instanceof PIXI.Sprite
let isDeepZoom = this.scatter.displayObject instanceof DeepZoomImage
let resolution = app.renderer.resolution
if (isSprite) {
w = this.scatter.displayObject.texture.width
h = this.scatter.displayObject.texture.height
} else if (isDeepZoom) {
let [ww, hh] = this.scatter.displayObject.baseSize
w = ww
h = hh
}
if (globalScatterLoaderCanvas === null) {
globalScatterLoaderCanvas = document.createElement('canvas')
}
let canvas = globalScatterLoaderCanvas
canvas.width = w
canvas.height = h
let renderer = new PIXI.WebGLRenderer(w, h, {
view: canvas,
resolution: resolution
})
let displayObject = this.scatter.displayObject
let x = displayObject.x
let y = displayObject.y
let rot = displayObject.rotation
let sx = displayObject.scale.x
let sy = displayObject.scale.y
displayObject.rotation = 0
// The Safari WebGLRenderer wants everything flipped
// See https://github.com/pixijs/pixi.js/issues/2283
displayObject.x = 0
if (Capabilities.isSafari) {
displayObject.y = h
displayObject.scale.set(1, -1) // sx, -sy)
} else {
displayObject.y = 0
displayObject.scale.set(1, 1)
}
if (isSprite) {
displayObject.width = w
displayObject.height = h
}
renderer.render(displayObject)
displayObject.rotation = rot
displayObject.x = x
displayObject.y = y
displayObject.scale.set(sx, sy)
let url = canvas.toDataURL()
return [x, y, w, h, url]
}
load(domNode) {
return new Promise((resolve, reject) => {
let isImage = domNode instanceof HTMLImageElement
let isSprite = this.scatter.displayObject instanceof PIXI.Sprite
let image = isImage ? domNode : document.createElement('img')
let [x, y, w, h, cloneURL] = this.cloneScatterImage()
let [ww, hh] = this.unscaledSize()
image.onload = e => {
if (!isImage) domNode.appendChild(image)
this.x = x
this.y = y
this.wantedWidth = ww
this.wantedHeight = hh
this.scale = 1
this.rotation = this.scatter.rotation
resolve(this)
}
image.onerror = e => {
reject(this)
}
image.src = cloneURL
})
}
}
export default class FlipEffect {
constructor(scatter, domScatterContainer, flipTemplate, backLoader) {
this.flipped = false
this.scatter = scatter
this.backLoader = backLoader
this.scatterLoader = new ScatterLoader(scatter)
this.domFlip = new DOMFlip(domScatterContainer, flipTemplate, this.scatterLoader, backLoader, {
onBack: this.backCardClosed.bind(this)
})
this.setupInfoButton()
}
startFlip() {
let center = this.flipCenter()
let loader = this.backLoader
this.domFlip.load().then(domFlip => {
this.scatter.displayObject.visible = false
domFlip.centerAt(center)
domFlip.zoom(this.scatter.scale)
let target = this.constraintFlipCenter(center, loader)
console.log('FlipEffect.startFlip', target, loader)
domFlip.start({ targetCenter: target })
})
}
unscaledSize() {
return this.scatterLoader.unscaledSize()
}
flipCenter() {
let isSprite = this.scatter.displayObject instanceof PIXI.Sprite
let resolution = isSprite ? app.renderer.resolution : 1
let center = this.scatter.center
let canvas = app.renderer.view
let domNode = this.domFlip.domScatterContainer.element
let page = window.convertPointFromNodeToPage(canvas, center.x * resolution, center.y * resolution)
let local = window.convertPointFromPageToNode(domNode, page.x, page.y)
return local
}
constraintFlipCenter(center, loader) {
let w = loader.wantedWidth
let h = loader.wantedHeight
console.log('constraintFlipCenter', w, h)
let canvas = app.renderer.view
let x = center.x
let y = center.y
if (x < w / 2) x = w / 2
if (y < h / 2) y = h / 2
if (x > canvas.width) x = canvas.width - w / 2
if (y > canvas.height) y = canvas.height - h / 2
return { x, y }
}
setupInfoButton() {
let iscale = 1.0 / this.scatter.scale
this.infoBtn = new PIXI.Graphics()
this.infoBtn.beginFill(0x333333)
this.infoBtn.lineStyle(4, 0xffffff)
this.infoBtn.drawCircle(0, 0, 22)
this.infoBtn.endFill()
this.infoBtn.beginFill(0xffffff)
this.infoBtn.lineStyle(0)
this.infoBtn.drawCircle(0, -8, 4)
this.infoBtn.endFill()
this.infoBtn.lineStyle(6, 0xffffff)
this.infoBtn.moveTo(0, -2)
this.infoBtn.lineTo(0, 14)
this.infoBtn.endFill()
this.infoBtn.on('click', e => {
this.infoSelected()
})
this.infoBtn.on('tap', e => {
this.infoSelected()
})
this.infoBtn.interactive = true
this.infoBtn.width = 44
this.infoBtn.height = 44
this.infoBtn.pivot.x = 30
this.infoBtn.pivot.y = 30
let displayObject = this.scatter.displayObject
let [w, h] = this.unscaledSize()
this.infoBtn.position = new PIXI.Point(w, h)
if (displayObject.foreground) {
this.infoBtn.scale.x = iscale
this.infoBtn.scale.y = iscale
displayObject.foreground.addChild(this.infoBtn)
} else {
displayObject.addChild(this.infoBtn)
}
this.scatter.addTransformEventCallback(e => {
let displayObject = this.scatter.displayObject
if (displayObject.foreground) {
if (e.scale) {
let iscale = 1.0 / e.scale
this.infoBtn.scale.x = iscale
this.infoBtn.scale.y = iscale
}
}
})
}
setupButton(url) {
let svgImage = new Image()
let canvas = document.createElement('canvas')
canvas.width = 88 * 4
canvas.height = 44 * 4
svgImage.onload = e => {
let displayObject = this.scatter.displayObject
canvas.getContext('2d').drawImage(svgImage, 0, 0, canvas.width, canvas.height)
let texure = new PIXI.Texture(new PIXI.BaseTexture(canvas))
this.infoBtn = new PIXI.Sprite(texure)
this.infoBtn.anchor.set(0.5, 0.5)
if (displayObject.foreground) {
displayObject.foreground.addChild(this.infoBtn)
} else {
displayObject.addChild(this.infoBtn)
}
this.infoBtn.scale.set(0.5, 0.5)
let [w, h] = this.unscaledSize()
this.infoBtn.position = new PIXI.Point(w, h)
this.infoBtn.interactive = true
this.infoBtn.updateTransform()
this.infoBtn.on('click', e => {
this.infoSelected()
})
this.infoBtn.on('tap', e => {
this.infoSelected()
})
}
svgImage.src = url
}
infoSelected() {
this.startFlip()
}
backSelected() {
this.domFlip.start()
}
backCardClosed() {
/*** The flip effect should now be in it's initial state again. All
memory should be freed. ***/
let displayObject = this.scatter.displayObject
displayObject.visible = true
this.domFlip.fadeOutAndRemove()
this.flipped = false
}
targetRotation(alpha) {
let ortho = 90
let rest = alpha % ortho
let delta = 0.0
if (rest > ortho / 2.0) {
delta = ortho - rest
} else {
delta = -rest
}
return delta
}
}