cm-chessboard 4.0.4 → 4.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -33
- package/assets/styles/_cm-chessboard-theme.scss +11 -2
- package/assets/styles/cm-chessboard.css +1 -1
- package/assets/styles/cm-chessboard.css.map +1 -1
- package/docs/PositionsAnimation.md +11 -0
- package/docs/PreMoves.md +1 -0
- package/examples/pieces-animation.html +25 -15
- package/examples/validate-moves.html +5 -7
- package/package.json +1 -1
- package/src/cm-chessboard/Chessboard.js +47 -86
- package/src/cm-chessboard/{ChessboardMoveInput.js → core/MoveInput.js} +14 -17
- package/src/cm-chessboard/{Position.js → core/Position.js} +26 -5
- package/src/cm-chessboard/core/PositionAnimationsQueue.js +274 -0
- package/src/cm-chessboard/{ChessboardState.js → core/State.js} +19 -12
- package/src/cm-chessboard/{ChessboardView.js → core/View.js} +29 -57
- package/src/cm-chessboard/{ChessboardViewAccessible.js → core/ViewAccessible.js} +6 -6
- package/test/TestPiecesAnimation.js +13 -16
- package/test/TestPosition.js +1 -1
- package/src/cm-chessboard/ChessboardPiecesAnimation.js +0 -142
- package/src/cm-chessboard/Observed.js +0 -40
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* License: MIT, see file 'LICENSE'
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {Svg} from "./
|
|
8
|
-
// import {Position} from "./Position.js"
|
|
7
|
+
import {Svg} from "./View.js"
|
|
9
8
|
|
|
10
9
|
const STATE = {
|
|
11
10
|
waitForInputStart: 0,
|
|
@@ -27,7 +26,7 @@ export const MOVE_CANCELED_REASON = {
|
|
|
27
26
|
|
|
28
27
|
const DRAG_THRESHOLD = 4
|
|
29
28
|
|
|
30
|
-
export class
|
|
29
|
+
export class MoveInput {
|
|
31
30
|
|
|
32
31
|
constructor(view, moveStartCallback, moveDoneCallback, moveCanceledCallback) {
|
|
33
32
|
this.view = view
|
|
@@ -102,7 +101,7 @@ export class ChessboardMoveInput {
|
|
|
102
101
|
this.draggablePiece = undefined
|
|
103
102
|
}
|
|
104
103
|
if (prevState === STATE.dragTo) {
|
|
105
|
-
this.view.setPieceVisibility(params.square)
|
|
104
|
+
this.view.setPieceVisibility(params.square, true)
|
|
106
105
|
}
|
|
107
106
|
break
|
|
108
107
|
|
|
@@ -139,21 +138,18 @@ export class ChessboardMoveInput {
|
|
|
139
138
|
}
|
|
140
139
|
this.toSquare = params.square
|
|
141
140
|
if (this.toSquare && this.moveDoneCallback(this.fromSquare, this.toSquare)) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
this.chessboard.state.position.setPiece(this.toSquare, this.movedPiece)
|
|
145
|
-
if (prevState === STATE.clickTo) {
|
|
146
|
-
this.updateStartEndMarkers()
|
|
147
|
-
this.view.animatePieces(prevSquares, this.chessboard.state.position.clone().squares, () => {
|
|
141
|
+
if(prevState === STATE.clickTo) {
|
|
142
|
+
this.chessboard.movePiece(this.fromSquare, this.toSquare, true).then(() => {
|
|
148
143
|
this.setMoveInputState(STATE.reset)
|
|
149
144
|
})
|
|
150
145
|
} else {
|
|
151
|
-
this.
|
|
152
|
-
|
|
153
|
-
|
|
146
|
+
this.chessboard.movePiece(this.fromSquare, this.toSquare, false).then(() => {
|
|
147
|
+
this.view.setPieceVisibility(this.toSquare, true)
|
|
148
|
+
this.setMoveInputState(STATE.reset)
|
|
149
|
+
})
|
|
154
150
|
}
|
|
155
151
|
} else {
|
|
156
|
-
this.view.
|
|
152
|
+
this.view.setPieceVisibility(this.fromSquare, true)
|
|
157
153
|
this.setMoveInputState(STATE.reset)
|
|
158
154
|
}
|
|
159
155
|
break
|
|
@@ -187,6 +183,7 @@ export class ChessboardMoveInput {
|
|
|
187
183
|
}
|
|
188
184
|
|
|
189
185
|
createDraggablePiece(pieceName) {
|
|
186
|
+
// TODO use the existing piece from the board and don't create an new one
|
|
190
187
|
if (this.draggablePiece) {
|
|
191
188
|
throw Error("draggablePiece exists")
|
|
192
189
|
}
|
|
@@ -255,7 +252,7 @@ export class ChessboardMoveInput {
|
|
|
255
252
|
const pieceColor = pieceName ? pieceName.substring(0, 1) : undefined
|
|
256
253
|
const startPieceName = this.chessboard.getPiece(this.fromSquare)
|
|
257
254
|
const startPieceColor = startPieceName ? startPieceName.substring(0, 1) : undefined
|
|
258
|
-
if (color && startPieceColor === pieceColor) {
|
|
255
|
+
if (color && startPieceColor === pieceColor) {
|
|
259
256
|
this.moveCanceledCallback(MOVE_CANCELED_REASON.clickedAnotherPiece, this.fromSquare, square)
|
|
260
257
|
if (this.moveStartCallback(square)) {
|
|
261
258
|
this.setMoveInputState(STATE.pieceClickedThreshold, {
|
|
@@ -356,13 +353,13 @@ export class ChessboardMoveInput {
|
|
|
356
353
|
this.moveCanceledCallback(MOVE_CANCELED_REASON.secondClick, square, square)
|
|
357
354
|
}
|
|
358
355
|
} else {
|
|
359
|
-
this.view.
|
|
356
|
+
this.view.redrawPieces()
|
|
360
357
|
const moveStartSquare = this.fromSquare
|
|
361
358
|
this.setMoveInputState(STATE.reset)
|
|
362
359
|
this.moveCanceledCallback(MOVE_CANCELED_REASON.movedOutOfBoard, moveStartSquare, undefined)
|
|
363
360
|
}
|
|
364
361
|
} else {
|
|
365
|
-
this.view.
|
|
362
|
+
this.view.redrawPieces()
|
|
366
363
|
this.setMoveInputState(STATE.reset)
|
|
367
364
|
}
|
|
368
365
|
}
|
|
@@ -12,8 +12,17 @@ export class Position {
|
|
|
12
12
|
this.squares = new Array(64).fill(null)
|
|
13
13
|
this.setFen(fen)
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
|
|
16
|
+
setFen(fen = FEN_EMPTY_POSITION) {
|
|
17
|
+
let fenNormalized
|
|
18
|
+
if (fen === "start") {
|
|
19
|
+
fenNormalized = FEN_START_POSITION
|
|
20
|
+
} else if (fen === "empty" || fen === undefined) {
|
|
21
|
+
fenNormalized = FEN_EMPTY_POSITION
|
|
22
|
+
} else {
|
|
23
|
+
fenNormalized = fen
|
|
24
|
+
}
|
|
25
|
+
const parts = fenNormalized.replace(/^\s*/, "").replace(/\s*$/, "").split(/\/|\s/)
|
|
17
26
|
for (let part = 0; part < 8; part++) {
|
|
18
27
|
const row = parts[7 - part].replace(/\d/g, (str) => {
|
|
19
28
|
const numSpaces = parseInt(str)
|
|
@@ -37,6 +46,7 @@ export class Position {
|
|
|
37
46
|
}
|
|
38
47
|
}
|
|
39
48
|
}
|
|
49
|
+
|
|
40
50
|
getFen() {
|
|
41
51
|
let parts = new Array(8).fill("")
|
|
42
52
|
for (let part = 0; part < 8; part++) {
|
|
@@ -66,9 +76,9 @@ export class Position {
|
|
|
66
76
|
}
|
|
67
77
|
return parts.join("/")
|
|
68
78
|
}
|
|
69
|
-
|
|
79
|
+
|
|
80
|
+
getPieces(sortBy = ['k', 'q', 'r', 'b', 'n', 'p']) {
|
|
70
81
|
const pieces = []
|
|
71
|
-
const sortBy = ['k', 'q', 'r', 'b', 'n', 'p']
|
|
72
82
|
const sort = (a, b) => {
|
|
73
83
|
return sortBy.indexOf(a.name) - sortBy.indexOf(b.name)
|
|
74
84
|
}
|
|
@@ -82,10 +92,21 @@ export class Position {
|
|
|
82
92
|
})
|
|
83
93
|
}
|
|
84
94
|
}
|
|
85
|
-
|
|
95
|
+
if(sortBy) {
|
|
96
|
+
pieces.sort(sort)
|
|
97
|
+
}
|
|
86
98
|
return pieces
|
|
87
99
|
}
|
|
88
100
|
|
|
101
|
+
movePiece(squareFrom, squareTo) {
|
|
102
|
+
if(!this.squares[Position.squareToIndex(squareFrom)]) {
|
|
103
|
+
console.error("movePiece, no piece on square", squareFrom)
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
this.squares[Position.squareToIndex(squareTo)] = this.squares[Position.squareToIndex(squareFrom)]
|
|
107
|
+
this.squares[Position.squareToIndex(squareFrom)] = undefined
|
|
108
|
+
}
|
|
109
|
+
|
|
89
110
|
setPiece(square, piece) {
|
|
90
111
|
this.squares[Position.squareToIndex(square)] = piece
|
|
91
112
|
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Author and copyright: Stefan Haack (https://shaack.com)
|
|
3
|
+
* Repository: https://github.com/shaack/cm-chessboard
|
|
4
|
+
* License: MIT, see file 'LICENSE'
|
|
5
|
+
*/
|
|
6
|
+
import {FEN_EMPTY_POSITION, Position} from "./Position.js"
|
|
7
|
+
import {Svg} from "./View.js"
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* Thanks to markosyan for the idea to the PromiseQueue
|
|
11
|
+
* https://medium.com/@karenmarkosyan/how-to-manage-promises-into-dynamic-queue-with-vanilla-javascript-9d0d1f8d4df5
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export class PromiseQueue {
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
this.queue = []
|
|
18
|
+
this.workingOnPromise = false
|
|
19
|
+
this.stop = false
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async enqueue(promise) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
this.queue.push({
|
|
25
|
+
promise, resolve, reject,
|
|
26
|
+
})
|
|
27
|
+
this.dequeue()
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
dequeue() {
|
|
32
|
+
if (this.workingOnPromise) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
if (this.stop) {
|
|
36
|
+
this.queue = []
|
|
37
|
+
this.stop = false
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
const entry = this.queue.shift()
|
|
41
|
+
if (!entry) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
this.workingOnPromise = true
|
|
46
|
+
entry.promise().then((value) => {
|
|
47
|
+
this.workingOnPromise = false
|
|
48
|
+
entry.resolve(value)
|
|
49
|
+
this.dequeue()
|
|
50
|
+
}).catch(err => {
|
|
51
|
+
this.workingOnPromise = false
|
|
52
|
+
entry.reject(err)
|
|
53
|
+
this.dequeue()
|
|
54
|
+
})
|
|
55
|
+
} catch (err) {
|
|
56
|
+
this.workingOnPromise = false
|
|
57
|
+
entry.reject(err)
|
|
58
|
+
this.dequeue()
|
|
59
|
+
}
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
destroy() {
|
|
64
|
+
console.log("PAQ destroy")
|
|
65
|
+
this.stop = true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
const CHANGE_TYPE = {
|
|
73
|
+
move: 0,
|
|
74
|
+
appear: 1,
|
|
75
|
+
disappear: 2
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class PositionsAnimation {
|
|
79
|
+
|
|
80
|
+
constructor(view, fromPosition, toPosition, duration, callback) {
|
|
81
|
+
this.view = view
|
|
82
|
+
if (fromPosition && toPosition) {
|
|
83
|
+
this.animatedElements = this.createAnimation(fromPosition.squares, toPosition.squares)
|
|
84
|
+
this.duration = duration
|
|
85
|
+
this.callback = callback
|
|
86
|
+
this.frameHandle = requestAnimationFrame(this.animationStep.bind(this))
|
|
87
|
+
} else {
|
|
88
|
+
console.error("fromPosition", fromPosition, "toPosition", toPosition)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static seekChanges(fromSquares, toSquares) {
|
|
93
|
+
const appearedList = [], disappearedList = [], changes = []
|
|
94
|
+
for (let i = 0; i < 64; i++) {
|
|
95
|
+
const previousSquare = fromSquares[i]
|
|
96
|
+
const newSquare = toSquares[i]
|
|
97
|
+
if (newSquare !== previousSquare) {
|
|
98
|
+
if (newSquare) {
|
|
99
|
+
appearedList.push({piece: newSquare, index: i})
|
|
100
|
+
}
|
|
101
|
+
if (previousSquare) {
|
|
102
|
+
disappearedList.push({piece: previousSquare, index: i})
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
appearedList.forEach((appeared) => {
|
|
107
|
+
let shortestDistance = 8
|
|
108
|
+
let foundMoved = undefined
|
|
109
|
+
disappearedList.forEach((disappeared) => {
|
|
110
|
+
if (appeared.piece === disappeared.piece) {
|
|
111
|
+
const moveDistance = PositionsAnimation.squareDistance(appeared.index, disappeared.index)
|
|
112
|
+
if (moveDistance < shortestDistance) {
|
|
113
|
+
foundMoved = disappeared
|
|
114
|
+
shortestDistance = moveDistance
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
if (foundMoved) {
|
|
119
|
+
disappearedList.splice(disappearedList.indexOf(foundMoved), 1) // remove from disappearedList, because it is moved now
|
|
120
|
+
changes.push({
|
|
121
|
+
type: CHANGE_TYPE.move,
|
|
122
|
+
piece: appeared.piece,
|
|
123
|
+
atIndex: foundMoved.index,
|
|
124
|
+
toIndex: appeared.index
|
|
125
|
+
})
|
|
126
|
+
} else {
|
|
127
|
+
changes.push({type: CHANGE_TYPE.appear, piece: appeared.piece, atIndex: appeared.index})
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
disappearedList.forEach((disappeared) => {
|
|
131
|
+
changes.push({type: CHANGE_TYPE.disappear, piece: disappeared.piece, atIndex: disappeared.index})
|
|
132
|
+
})
|
|
133
|
+
return changes
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
createAnimation(fromSquares, toSquares) {
|
|
137
|
+
const changes = PositionsAnimation.seekChanges(fromSquares, toSquares)
|
|
138
|
+
// console.log("changes", changes)
|
|
139
|
+
const animatedElements = []
|
|
140
|
+
changes.forEach((change) => {
|
|
141
|
+
const animatedItem = {
|
|
142
|
+
type: change.type
|
|
143
|
+
}
|
|
144
|
+
switch (change.type) {
|
|
145
|
+
case CHANGE_TYPE.move:
|
|
146
|
+
animatedItem.element = this.view.getPieceElement(Position.indexToSquare(change.atIndex))
|
|
147
|
+
animatedItem.element.parentNode.appendChild(animatedItem.element) // move element to top layer
|
|
148
|
+
animatedItem.atPoint = this.view.indexToPoint(change.atIndex)
|
|
149
|
+
animatedItem.toPoint = this.view.indexToPoint(change.toIndex)
|
|
150
|
+
break
|
|
151
|
+
case CHANGE_TYPE.appear:
|
|
152
|
+
animatedItem.element = this.view.drawPiece(Position.indexToSquare(change.atIndex), change.piece)
|
|
153
|
+
animatedItem.element.style.opacity = 0
|
|
154
|
+
break
|
|
155
|
+
case CHANGE_TYPE.disappear:
|
|
156
|
+
animatedItem.element = this.view.getPieceElement(Position.indexToSquare(change.atIndex))
|
|
157
|
+
break
|
|
158
|
+
}
|
|
159
|
+
animatedElements.push(animatedItem)
|
|
160
|
+
})
|
|
161
|
+
return animatedElements
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
animationStep(time) {
|
|
165
|
+
// console.log("animationStep", time)
|
|
166
|
+
if (!this.startTime) {
|
|
167
|
+
this.startTime = time
|
|
168
|
+
}
|
|
169
|
+
const timeDiff = time - this.startTime
|
|
170
|
+
if (timeDiff <= this.duration) {
|
|
171
|
+
this.frameHandle = requestAnimationFrame(this.animationStep.bind(this))
|
|
172
|
+
} else {
|
|
173
|
+
cancelAnimationFrame(this.frameHandle)
|
|
174
|
+
// console.log("ANIMATION FINISHED")
|
|
175
|
+
this.animatedElements.forEach((animatedItem) => {
|
|
176
|
+
if (animatedItem.type === CHANGE_TYPE.disappear) {
|
|
177
|
+
Svg.removeElement(animatedItem.element)
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
this.callback()
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
const t = Math.min(1, timeDiff / this.duration)
|
|
184
|
+
let progress = t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t // easeInOut
|
|
185
|
+
if (isNaN(progress)) {
|
|
186
|
+
progress = 1
|
|
187
|
+
}
|
|
188
|
+
this.animatedElements.forEach((animatedItem) => {
|
|
189
|
+
// console.log("animatedItem", animatedItem)
|
|
190
|
+
if (animatedItem.element) {
|
|
191
|
+
switch (animatedItem.type) {
|
|
192
|
+
case CHANGE_TYPE.move:
|
|
193
|
+
animatedItem.element.transform.baseVal.removeItem(0)
|
|
194
|
+
const transform = (this.view.svg.createSVGTransform())
|
|
195
|
+
transform.setTranslate(
|
|
196
|
+
animatedItem.atPoint.x + (animatedItem.toPoint.x - animatedItem.atPoint.x) * progress,
|
|
197
|
+
animatedItem.atPoint.y + (animatedItem.toPoint.y - animatedItem.atPoint.y) * progress)
|
|
198
|
+
animatedItem.element.transform.baseVal.appendItem(transform)
|
|
199
|
+
break
|
|
200
|
+
case CHANGE_TYPE.appear:
|
|
201
|
+
animatedItem.element.style.opacity = Math.round(progress * 100) / 100
|
|
202
|
+
break
|
|
203
|
+
case CHANGE_TYPE.disappear:
|
|
204
|
+
animatedItem.element.style.opacity = Math.round((1 - progress) * 100) / 100
|
|
205
|
+
break
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
console.warn("animatedItem has no element", animatedItem)
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
static squareDistance(index1, index2) {
|
|
214
|
+
const file1 = index1 % 8
|
|
215
|
+
const rank1 = Math.floor(index1 / 8)
|
|
216
|
+
const file2 = index2 % 8
|
|
217
|
+
const rank2 = Math.floor(index2 / 8)
|
|
218
|
+
return Math.max(Math.abs(rank2 - rank1), Math.abs(file2 - file1))
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export class PositionAnimationsQueue extends PromiseQueue {
|
|
224
|
+
|
|
225
|
+
constructor(chessboard) {
|
|
226
|
+
super()
|
|
227
|
+
this.chessboard = chessboard
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async enqueuePositionChange(positionFrom, positionTo, animated) {
|
|
231
|
+
return super.enqueue(() => new Promise((resolve) => {
|
|
232
|
+
let duration = animated ? this.chessboard.props.animationDuration : 0
|
|
233
|
+
if(this.queue.length > 0) {
|
|
234
|
+
duration = duration / (1 + Math.pow(this.queue.length / 5, 2))
|
|
235
|
+
}
|
|
236
|
+
// console.log("duration", duration, animated, "this.chessboard.props.animationDuration", this.chessboard.props.animationDuration)
|
|
237
|
+
new PositionsAnimation(this.chessboard.view,
|
|
238
|
+
positionFrom, positionTo, animated ? duration : 0,
|
|
239
|
+
() => {
|
|
240
|
+
if(this.chessboard.view) { // if destroyed, no view anymore
|
|
241
|
+
this.chessboard.view.redrawPieces(positionTo.squares)
|
|
242
|
+
}
|
|
243
|
+
resolve()
|
|
244
|
+
}
|
|
245
|
+
)
|
|
246
|
+
}))
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async enqueueTurnBoard(position, color, animated) {
|
|
250
|
+
return super.enqueue(() => new Promise((resolve) => {
|
|
251
|
+
const emptyPosition = new Position(FEN_EMPTY_POSITION)
|
|
252
|
+
let duration = animated ? this.chessboard.props.animationDuration : 0
|
|
253
|
+
if(this.queue.length > 0) {
|
|
254
|
+
duration = duration / (1 + Math.pow(this.queue.length / 5, 2))
|
|
255
|
+
}
|
|
256
|
+
new PositionsAnimation(this.chessboard.view,
|
|
257
|
+
position, emptyPosition, animated ? duration : 0,
|
|
258
|
+
() => {
|
|
259
|
+
this.chessboard.state.orientation = color
|
|
260
|
+
this.chessboard.view.redraw()
|
|
261
|
+
this.chessboard.view.redrawPieces(emptyPosition.squares)
|
|
262
|
+
new PositionsAnimation(this.chessboard.view,
|
|
263
|
+
emptyPosition, position, animated ? duration : 0,
|
|
264
|
+
() => {
|
|
265
|
+
this.chessboard.view.redrawPieces(position.squares)
|
|
266
|
+
resolve()
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
)
|
|
271
|
+
}))
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import {Position} from "./Position.js"
|
|
7
7
|
|
|
8
|
-
export class
|
|
8
|
+
export class State {
|
|
9
9
|
|
|
10
10
|
constructor() {
|
|
11
11
|
this.position = new Position()
|
|
@@ -16,21 +16,28 @@ export class ChessboardState {
|
|
|
16
16
|
this.inputEnabled = false
|
|
17
17
|
this.squareSelectEnabled = false
|
|
18
18
|
}
|
|
19
|
-
/*
|
|
20
|
-
getPieces() {
|
|
21
|
-
return this.position.getPieces()
|
|
22
|
-
}
|
|
23
19
|
|
|
24
|
-
|
|
25
|
-
this.position
|
|
20
|
+
setPosition(fen, animated = false) {
|
|
21
|
+
this.position = new Position(fen, animated)
|
|
26
22
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.
|
|
23
|
+
|
|
24
|
+
movePiece(fromSquare, toSquare, animated = false) {
|
|
25
|
+
const position = this._position.clone()
|
|
26
|
+
position.animated = animated
|
|
27
|
+
const piece = position.getPiece(fromSquare)
|
|
28
|
+
if(!piece) {
|
|
29
|
+
console.error("no piece on", fromSquare)
|
|
30
|
+
}
|
|
31
|
+
position.setPiece(fromSquare, undefined)
|
|
32
|
+
position.setPiece(toSquare, piece)
|
|
33
|
+
this._position = position
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
setPiece(square, piece, animated = false) {
|
|
37
|
+
const position = this._position.clone()
|
|
38
|
+
position.animated = animated
|
|
39
|
+
position.setPiece(square, piece)
|
|
40
|
+
this._position = position
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
addMarker(square, type) {
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
* License: MIT, see file 'LICENSE'
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import {COLOR, INPUT_EVENT_TYPE, BORDER_TYPE} from "
|
|
9
|
-
import {ChessboardPiecesAnimation} from "./ChessboardPiecesAnimation.js"
|
|
7
|
+
import {MoveInput} from "./MoveInput.js"
|
|
8
|
+
import {COLOR, INPUT_EVENT_TYPE, BORDER_TYPE} from "../Chessboard.js"
|
|
10
9
|
import {Position} from "./Position.js"
|
|
11
10
|
|
|
12
11
|
export const piecesTranslations = {
|
|
@@ -42,33 +41,28 @@ export const piecesTranslations = {
|
|
|
42
41
|
|
|
43
42
|
export function renderPieceTitle(lang, name, color = undefined) {
|
|
44
43
|
let title = piecesTranslations[lang].pieces_long[name]
|
|
45
|
-
if(color) {
|
|
44
|
+
if (color) {
|
|
46
45
|
title += " " + piecesTranslations[lang].colors_long[color]
|
|
47
46
|
}
|
|
48
47
|
return title
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
export class
|
|
50
|
+
export class View {
|
|
52
51
|
|
|
53
52
|
constructor(chessboard) {
|
|
54
|
-
this.animationRunning = false
|
|
55
|
-
this.currentAnimation = undefined
|
|
56
53
|
this.chessboard = chessboard
|
|
57
|
-
this.moveInput = new
|
|
54
|
+
this.moveInput = new MoveInput(this,
|
|
58
55
|
this.moveStartCallback.bind(this),
|
|
59
56
|
this.moveDoneCallback.bind(this),
|
|
60
57
|
this.moveCanceledCallback.bind(this)
|
|
61
58
|
)
|
|
62
|
-
this.animationQueue = []
|
|
63
59
|
if (chessboard.props.sprite.cache) {
|
|
64
60
|
this.cacheSprite()
|
|
65
61
|
}
|
|
66
62
|
this.context = document.createElement("div")
|
|
67
63
|
this.chessboard.context.appendChild(this.context)
|
|
68
64
|
if (chessboard.props.responsive) {
|
|
69
|
-
// noinspection JSUnresolvedVariable
|
|
70
65
|
if (typeof ResizeObserver !== "undefined") {
|
|
71
|
-
// noinspection JSUnresolvedFunction
|
|
72
66
|
this.resizeObserver = new ResizeObserver(() => {
|
|
73
67
|
this.handleResize()
|
|
74
68
|
})
|
|
@@ -104,11 +98,11 @@ export class ChessboardView {
|
|
|
104
98
|
}
|
|
105
99
|
this.chessboard.context.removeEventListener("mousedown", this.pointerDownListener)
|
|
106
100
|
this.chessboard.context.removeEventListener("touchstart", this.pointerDownListener)
|
|
107
|
-
Svg.removeElement(this.svg)
|
|
108
101
|
this.animationQueue = []
|
|
109
102
|
if (this.currentAnimation) {
|
|
110
103
|
cancelAnimationFrame(this.currentAnimation.frameHandle)
|
|
111
104
|
}
|
|
105
|
+
Svg.removeElement(this.svg)
|
|
112
106
|
}
|
|
113
107
|
|
|
114
108
|
// Sprite //
|
|
@@ -172,6 +166,7 @@ export class ChessboardView {
|
|
|
172
166
|
this.context.clientHeight !== this.height) {
|
|
173
167
|
this.updateMetrics()
|
|
174
168
|
this.redraw()
|
|
169
|
+
this.redrawPieces()
|
|
175
170
|
}
|
|
176
171
|
this.svg.setAttribute("width", "100%") // safari bugfix
|
|
177
172
|
this.svg.setAttribute("height", "100%")
|
|
@@ -182,7 +177,6 @@ export class ChessboardView {
|
|
|
182
177
|
this.drawCoordinates()
|
|
183
178
|
this.drawMarkers()
|
|
184
179
|
this.visualizeInputState()
|
|
185
|
-
this.drawPieces(this.chessboard.state.position.squares)
|
|
186
180
|
}
|
|
187
181
|
|
|
188
182
|
// Board //
|
|
@@ -191,20 +185,20 @@ export class ChessboardView {
|
|
|
191
185
|
while (this.boardGroup.firstChild) {
|
|
192
186
|
this.boardGroup.removeChild(this.boardGroup.lastChild)
|
|
193
187
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
188
|
+
|
|
189
|
+
let boardBorder = Svg.addElement(this.boardGroup, "rect", {width: this.width, height: this.height})
|
|
190
|
+
boardBorder.setAttribute("class", "border")
|
|
191
|
+
if (this.chessboard.props.style.borderType === BORDER_TYPE.frame) {
|
|
192
|
+
const innerPos = this.borderSize
|
|
193
|
+
let borderInner = Svg.addElement(this.boardGroup, "rect", {
|
|
194
|
+
x: innerPos,
|
|
195
|
+
y: innerPos,
|
|
196
|
+
width: this.width - innerPos * 2,
|
|
197
|
+
height: this.height - innerPos * 2
|
|
198
|
+
})
|
|
199
|
+
borderInner.setAttribute("class", "border-inner")
|
|
207
200
|
}
|
|
201
|
+
|
|
208
202
|
for (let i = 0; i < 64; i++) {
|
|
209
203
|
const index = this.chessboard.state.orientation === COLOR.white ? i : 63 - i
|
|
210
204
|
const squareColor = ((9 * index) & 8) === 0 ? 'black' : 'white'
|
|
@@ -276,7 +270,7 @@ export class ChessboardView {
|
|
|
276
270
|
|
|
277
271
|
// Pieces //
|
|
278
272
|
|
|
279
|
-
|
|
273
|
+
redrawPieces(squares = this.chessboard.state.position.squares) {
|
|
280
274
|
const childNodes = Array.from(this.piecesGroup.childNodes)
|
|
281
275
|
for (let i = 0; i < 64; i++) {
|
|
282
276
|
const pieceName = squares[i]
|
|
@@ -314,7 +308,7 @@ export class ChessboardView {
|
|
|
314
308
|
}
|
|
315
309
|
|
|
316
310
|
setPieceVisibility(square, visible = true) {
|
|
317
|
-
const piece = this.
|
|
311
|
+
const piece = this.getPieceElement(square)
|
|
318
312
|
if (visible) {
|
|
319
313
|
piece.setAttribute("visibility", "visible")
|
|
320
314
|
} else {
|
|
@@ -322,11 +316,15 @@ export class ChessboardView {
|
|
|
322
316
|
}
|
|
323
317
|
}
|
|
324
318
|
|
|
325
|
-
|
|
326
|
-
if(square.length < 2) {
|
|
319
|
+
getPieceElement(square) {
|
|
320
|
+
if (square.length < 2) {
|
|
327
321
|
throw new Error("980e03")
|
|
328
322
|
}
|
|
329
|
-
|
|
323
|
+
const piece = this.piecesGroup.querySelector(`g[data-square='${square}']`)
|
|
324
|
+
if (!piece) {
|
|
325
|
+
console.error("no piece found on", square)
|
|
326
|
+
}
|
|
327
|
+
return piece
|
|
330
328
|
}
|
|
331
329
|
|
|
332
330
|
// Markers //
|
|
@@ -358,32 +356,6 @@ export class ChessboardView {
|
|
|
358
356
|
return markerGroup
|
|
359
357
|
}
|
|
360
358
|
|
|
361
|
-
// animation queue //
|
|
362
|
-
|
|
363
|
-
animatePieces(fromSquares, toSquares, callback) {
|
|
364
|
-
this.animationQueue.push({fromSquares: fromSquares, toSquares: toSquares, callback: callback})
|
|
365
|
-
if (!this.animationRunning) {
|
|
366
|
-
this.nextPieceAnimationInQueue()
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
nextPieceAnimationInQueue() {
|
|
371
|
-
const nextAnimation = this.animationQueue.shift()
|
|
372
|
-
if (nextAnimation !== undefined) {
|
|
373
|
-
this.animationRunning = true
|
|
374
|
-
this.currentAnimation = new ChessboardPiecesAnimation(this, nextAnimation.fromSquares, nextAnimation.toSquares, this.chessboard.props.animationDuration / (this.animationQueue.length + 1), () => {
|
|
375
|
-
if (!this.moveInput.draggablePiece) {
|
|
376
|
-
this.drawPieces(nextAnimation.toSquares)
|
|
377
|
-
}
|
|
378
|
-
this.animationRunning = false
|
|
379
|
-
this.nextPieceAnimationInQueue()
|
|
380
|
-
if (nextAnimation.callback) {
|
|
381
|
-
nextAnimation.callback()
|
|
382
|
-
}
|
|
383
|
-
})
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
359
|
// enable and disable move input //
|
|
388
360
|
|
|
389
361
|
enableMoveInput(eventHandler, color = undefined) {
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Repository: https://github.com/shaack/cm-chessboard
|
|
4
4
|
* License: MIT, see file 'LICENSE'
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import {COLOR, INPUT_EVENT_TYPE} from "
|
|
8
|
-
import {piecesTranslations} from "./
|
|
6
|
+
import {View, renderPieceTitle} from "./View.js"
|
|
7
|
+
import {COLOR, INPUT_EVENT_TYPE} from "../Chessboard.js"
|
|
8
|
+
import {piecesTranslations} from "./View.js"
|
|
9
9
|
|
|
10
10
|
const hlTranslations = {
|
|
11
11
|
de: {
|
|
@@ -32,7 +32,7 @@ const hlTranslations = {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export class
|
|
35
|
+
export class ViewAccessible extends View {
|
|
36
36
|
|
|
37
37
|
constructor(chessboard, callbackAfterCreation) {
|
|
38
38
|
super(chessboard, callbackAfterCreation)
|
|
@@ -103,8 +103,8 @@ export class ChessboardViewAccessible extends ChessboardView {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
super.
|
|
106
|
+
redrawPieces(squares = this.chessboard.state.position.squares) {
|
|
107
|
+
super.redrawPieces(squares)
|
|
108
108
|
setTimeout(() => {
|
|
109
109
|
if(this.chessboard.props.accessibility.boardAsTable) {
|
|
110
110
|
this.redrawBoardAsTable()
|