cm-chessboard 4.0.2 → 4.0.6
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/docs/PositionsAnimation.md +11 -0
- package/docs/PreMoves.md +3 -0
- package/examples/accessible-chessboard.html +1 -1
- package/examples/enable-input.html +1 -1
- 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 -91
- package/src/cm-chessboard/{ChessboardMoveInput.js → core/MoveInput.js} +16 -19
- 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} +16 -45
- 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
|
@@ -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 = {
|
|
@@ -48,13 +47,13 @@ export function renderPieceTitle(lang, name, color = undefined) {
|
|
|
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
|
|
53
|
+
// this.animationRunning = false
|
|
54
|
+
// this.currentAnimation = undefined
|
|
56
55
|
this.chessboard = chessboard
|
|
57
|
-
this.moveInput = new
|
|
56
|
+
this.moveInput = new MoveInput(this,
|
|
58
57
|
this.moveStartCallback.bind(this),
|
|
59
58
|
this.moveDoneCallback.bind(this),
|
|
60
59
|
this.moveCanceledCallback.bind(this)
|
|
@@ -104,11 +103,11 @@ export class ChessboardView {
|
|
|
104
103
|
}
|
|
105
104
|
this.chessboard.context.removeEventListener("mousedown", this.pointerDownListener)
|
|
106
105
|
this.chessboard.context.removeEventListener("touchstart", this.pointerDownListener)
|
|
107
|
-
Svg.removeElement(this.svg)
|
|
108
106
|
this.animationQueue = []
|
|
109
107
|
if (this.currentAnimation) {
|
|
110
108
|
cancelAnimationFrame(this.currentAnimation.frameHandle)
|
|
111
109
|
}
|
|
110
|
+
Svg.removeElement(this.svg)
|
|
112
111
|
}
|
|
113
112
|
|
|
114
113
|
// Sprite //
|
|
@@ -172,6 +171,7 @@ export class ChessboardView {
|
|
|
172
171
|
this.context.clientHeight !== this.height) {
|
|
173
172
|
this.updateMetrics()
|
|
174
173
|
this.redraw()
|
|
174
|
+
this.redrawPieces()
|
|
175
175
|
}
|
|
176
176
|
this.svg.setAttribute("width", "100%") // safari bugfix
|
|
177
177
|
this.svg.setAttribute("height", "100%")
|
|
@@ -182,7 +182,6 @@ export class ChessboardView {
|
|
|
182
182
|
this.drawCoordinates()
|
|
183
183
|
this.drawMarkers()
|
|
184
184
|
this.visualizeInputState()
|
|
185
|
-
this.drawPieces(this.chessboard.state.position.squares)
|
|
186
185
|
}
|
|
187
186
|
|
|
188
187
|
// Board //
|
|
@@ -276,7 +275,7 @@ export class ChessboardView {
|
|
|
276
275
|
|
|
277
276
|
// Pieces //
|
|
278
277
|
|
|
279
|
-
|
|
278
|
+
redrawPieces(squares = this.chessboard.state.position.squares) {
|
|
280
279
|
const childNodes = Array.from(this.piecesGroup.childNodes)
|
|
281
280
|
for (let i = 0; i < 64; i++) {
|
|
282
281
|
const pieceName = squares[i]
|
|
@@ -314,7 +313,7 @@ export class ChessboardView {
|
|
|
314
313
|
}
|
|
315
314
|
|
|
316
315
|
setPieceVisibility(square, visible = true) {
|
|
317
|
-
const piece = this.
|
|
316
|
+
const piece = this.getPieceElement(square)
|
|
318
317
|
if (visible) {
|
|
319
318
|
piece.setAttribute("visibility", "visible")
|
|
320
319
|
} else {
|
|
@@ -322,11 +321,15 @@ export class ChessboardView {
|
|
|
322
321
|
}
|
|
323
322
|
}
|
|
324
323
|
|
|
325
|
-
|
|
324
|
+
getPieceElement(square) {
|
|
326
325
|
if(square.length < 2) {
|
|
327
326
|
throw new Error("980e03")
|
|
328
327
|
}
|
|
329
|
-
|
|
328
|
+
const piece = this.piecesGroup.querySelector(`g[data-square='${square}']`)
|
|
329
|
+
if(!piece) {
|
|
330
|
+
console.error("no piece found on", square)
|
|
331
|
+
}
|
|
332
|
+
return piece
|
|
330
333
|
}
|
|
331
334
|
|
|
332
335
|
// Markers //
|
|
@@ -358,38 +361,6 @@ export class ChessboardView {
|
|
|
358
361
|
return markerGroup
|
|
359
362
|
}
|
|
360
363
|
|
|
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
|
-
this.animationRunning = false
|
|
378
|
-
this.nextPieceAnimationInQueue()
|
|
379
|
-
if (nextAnimation.callback) {
|
|
380
|
-
nextAnimation.callback()
|
|
381
|
-
}
|
|
382
|
-
} else {
|
|
383
|
-
this.animationRunning = false
|
|
384
|
-
this.nextPieceAnimationInQueue()
|
|
385
|
-
if (nextAnimation.callback) {
|
|
386
|
-
nextAnimation.callback()
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
})
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
364
|
// enable and disable move input //
|
|
394
365
|
|
|
395
366
|
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()
|
|
@@ -5,32 +5,29 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {describe, it, assert} from "../node_modules/teevi/src/teevi.js"
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {ViewMock} from "./mocks/ViewMock.js"
|
|
11
|
-
|
|
12
|
-
const cfa = new ChessboardPiecesAnimation(new ViewMock())
|
|
8
|
+
import {State} from "../src/cm-chessboard/core/State.js"
|
|
9
|
+
import {PositionsAnimation} from "../src/cm-chessboard/core/PositionAnimationsQueue.js"
|
|
13
10
|
|
|
14
11
|
describe("TestPiecesAnimation", () => {
|
|
15
12
|
it("should calculate square distances", () => {
|
|
16
|
-
assert.equals(
|
|
17
|
-
assert.equals(
|
|
18
|
-
assert.equals(
|
|
19
|
-
assert.equals(
|
|
20
|
-
assert.equals(
|
|
21
|
-
assert.equals(
|
|
22
|
-
assert.equals(
|
|
23
|
-
assert.equals(
|
|
13
|
+
assert.equals(PositionsAnimation.squareDistance(0, 0), 0)
|
|
14
|
+
assert.equals(PositionsAnimation.squareDistance(0, 1), 1)
|
|
15
|
+
assert.equals(PositionsAnimation.squareDistance(0, 7), 7)
|
|
16
|
+
assert.equals(PositionsAnimation.squareDistance(0, 8), 1)
|
|
17
|
+
assert.equals(PositionsAnimation.squareDistance(10, 20), 2)
|
|
18
|
+
assert.equals(PositionsAnimation.squareDistance(0, 63), 7)
|
|
19
|
+
assert.equals(PositionsAnimation.squareDistance(8, 24), 2)
|
|
20
|
+
assert.equals(PositionsAnimation.squareDistance(14, 24), 6)
|
|
24
21
|
})
|
|
25
22
|
|
|
26
23
|
it("should seek changes", () => {
|
|
27
|
-
const state1 = new
|
|
24
|
+
const state1 = new State()
|
|
28
25
|
state1.setPosition("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR")
|
|
29
|
-
const state2 = new
|
|
26
|
+
const state2 = new State()
|
|
30
27
|
state2.setPosition("rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR")
|
|
31
28
|
const previousBoard1 = state1.position.squares
|
|
32
29
|
const newBoard1 = state2.position.squares
|
|
33
|
-
const changes =
|
|
30
|
+
const changes = PositionsAnimation.seekChanges(previousBoard1, newBoard1)
|
|
34
31
|
|
|
35
32
|
assert.equals(changes[0].type,0 )
|
|
36
33
|
assert.equals(changes[0].piece, "wn")
|
package/test/TestPosition.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {describe, it, assert} from "../node_modules/teevi/src/teevi.js"
|
|
8
|
-
import {Position} from "../src/cm-chessboard/Position.js"
|
|
8
|
+
import {Position} from "../src/cm-chessboard/core/Position.js"
|
|
9
9
|
|
|
10
10
|
describe("TestPosition", () => {
|
|
11
11
|
it("should convert square to index", () => {
|