UNPKG

@noe_rls/cm-chessboard

Version:

A JavaScript chessboard which is lightweight, ES6 module based, responsive, SVG rendered and without dependencies.

141 lines (133 loc) 5.61 kB
/** * Author and copyright: Stefan Haack (https://shaack.com) * Repository: https://github.com/shaack/cm-chessboard * License: MIT, see file 'LICENSE' */ const CHANGE_TYPE = { move: 0, appear: 1, disappear: 2 } export class ChessboardPiecesAnimation { constructor(view, fromSquares, toSquares, duration, callback) { this.view = view if (fromSquares && toSquares) { this.animatedElements = this.createAnimation(fromSquares, toSquares) this.duration = duration this.callback = callback this.frameHandle = requestAnimationFrame(this.animationStep.bind(this)) } } seekChanges(fromSquares, toSquares) { const appearedList = [], disappearedList = [], changes = [] for (let i = 0; i < 64; i++) { const previousSquare = fromSquares[i] const newSquare = toSquares[i] if (newSquare !== previousSquare) { if (newSquare) { appearedList.push({piece: newSquare, index: i}) } if (previousSquare) { disappearedList.push({piece: previousSquare, index: i}) } } } appearedList.forEach((appeared) => { let shortestDistance = 8 let foundMoved = undefined disappearedList.forEach((disappeared) => { if (appeared.piece === disappeared.piece) { const moveDistance = this.squareDistance(appeared.index, disappeared.index) if (moveDistance < shortestDistance) { foundMoved = disappeared shortestDistance = moveDistance } } }) if (foundMoved) { disappearedList.splice(disappearedList.indexOf(foundMoved), 1) // remove from disappearedList, because it is moved now changes.push({ type: CHANGE_TYPE.move, piece: appeared.piece, atIndex: foundMoved.index, toIndex: appeared.index }) } else { changes.push({type: CHANGE_TYPE.appear, piece: appeared.piece, atIndex: appeared.index}) } }) disappearedList.forEach((disappeared) => { changes.push({type: CHANGE_TYPE.disappear, piece: disappeared.piece, atIndex: disappeared.index}) }) return changes } createAnimation(fromSquares, toSquares) { const changes = this.seekChanges(fromSquares, toSquares) const animatedElements = [] changes.forEach((change) => { const animatedItem = { type: change.type } switch (change.type) { case CHANGE_TYPE.move: animatedItem.element = this.view.getPiece(change.atIndex) animatedItem.atPoint = this.view.squareIndexToPoint(change.atIndex) animatedItem.toPoint = this.view.squareIndexToPoint(change.toIndex) break case CHANGE_TYPE.appear: animatedItem.element = this.view.drawPiece(change.atIndex, change.piece) animatedItem.element.style.opacity = 0 break case CHANGE_TYPE.disappear: animatedItem.element = this.view.getPiece(change.atIndex) break } animatedElements.push(animatedItem) }) return animatedElements } animationStep(time) { if (!this.startTime) { this.startTime = time } const timeDiff = time - this.startTime if (timeDiff <= this.duration) { this.frameHandle = requestAnimationFrame(this.animationStep.bind(this)) } else { cancelAnimationFrame(this.frameHandle) this.callback() return } const t = Math.min(1, timeDiff / this.duration) const progress = t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t // easeInOut this.animatedElements.forEach((animatedItem) => { if (animatedItem.element) { switch (animatedItem.type) { case CHANGE_TYPE.move: animatedItem.element.transform.baseVal.removeItem(0) const transform = (this.view.svg.createSVGTransform()) transform.setTranslate( animatedItem.atPoint.x + (animatedItem.toPoint.x - animatedItem.atPoint.x) * progress, animatedItem.atPoint.y + (animatedItem.toPoint.y - animatedItem.atPoint.y) * progress) animatedItem.element.transform.baseVal.appendItem(transform) break case CHANGE_TYPE.appear: animatedItem.element.style.opacity = progress break case CHANGE_TYPE.disappear: animatedItem.element.style.opacity = 1 - progress break } } else { console.warn("animatedItem has no element", animatedItem) } }) } squareDistance(index1, index2) { const file1 = index1 % 8 const rank1 = Math.floor(index1 / 8) const file2 = index2 % 8 const rank2 = Math.floor(index2 / 8) return Math.max(Math.abs(rank2 - rank1), Math.abs(file2 - file1)) } }