cm-chessboard 8.4.1 → 8.5.0
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
CHANGED
|
@@ -24,7 +24,6 @@ The core of cm-chessboard is small, fast and reduced to the essentials. You can
|
|
|
24
24
|
- [Arrows Extension](https://shaack.com/projekte/cm-chessboard/examples/extensions/arrows-extension.html) ⇨ renders arrows on the chessboard
|
|
25
25
|
- [Accessibility Extension](https://shaack.com/projekte/cm-chessboard/examples/extensions/accessibility-extension.html) ⇨ makes the chessboard more accessible
|
|
26
26
|
- [PromotionDialog Extension](https://shaack.com/projekte/cm-chessboard/examples/extensions/promotion-dialog-extension.html) ⇨ shows a dialog to select the piece to promote to
|
|
27
|
-
- [RenderVideo Extension](https://shaack.com/projekte/cm-chessboard/examples/extensions/render-video-extension.html) ⇨ renders a video from the pieces movement on the board (beta version for now)
|
|
28
27
|
|
|
29
28
|
## Demo and repository
|
|
30
29
|
|
package/index.html
CHANGED
|
@@ -32,7 +32,6 @@ It works, it is cool and it is easy to use. 👍</p>
|
|
|
32
32
|
<li><a href="examples/extensions/html-layer-extension.html">HTML Layer extension</a></li>
|
|
33
33
|
<li><a href="examples/extensions/promotion-dialog-extension.html">PromotionDialog extension</a></li>
|
|
34
34
|
<li><a href="examples/extensions/accessibility-extension.html">Accessibility extension</a></li>
|
|
35
|
-
<li><a href="examples/extensions/render-video-extension.html">RenderVideo extension</a></li>
|
|
36
35
|
</ul>
|
|
37
36
|
</div>
|
|
38
37
|
<h2>Chessboard</h2>
|
package/package.json
CHANGED
|
@@ -39,6 +39,7 @@ export class ChessboardView {
|
|
|
39
39
|
this.container.addEventListener("mousedown", this.pointerDownListener)
|
|
40
40
|
this.container.addEventListener("touchstart", this.pointerDownListener, {passive: false})
|
|
41
41
|
this.createSvgAndGroups()
|
|
42
|
+
this.handleResize()
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
pointerDownHandler(e) {
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<title>cm-chessboard</title>
|
|
6
|
-
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0"/>
|
|
7
|
-
<link rel="stylesheet" href="../styles/examples.css"/>
|
|
8
|
-
<link rel="stylesheet" href="../../assets/chessboard.css"/>
|
|
9
|
-
</head>
|
|
10
|
-
<body>
|
|
11
|
-
<h1><a href="../..">cm-chessboard</a></h1>
|
|
12
|
-
<h2>Example: RenderVideo extension</h2>
|
|
13
|
-
<p>With this extension you can render a video from the pieces movement on the board.</p>
|
|
14
|
-
<p>It renders .mp4 files in macOS Safari and .webm files in Chrome Browser.</p>
|
|
15
|
-
<div class="board board-small" id="board"></div>
|
|
16
|
-
<video id="videoOut" controls="controls"></video>
|
|
17
|
-
<br/>
|
|
18
|
-
<a href="" style="visibility: hidden" id="download-link">Download video</a>
|
|
19
|
-
<script type="module">
|
|
20
|
-
import {RenderVideo} from "../../src/extensions/render-video/RenderVideo.js"
|
|
21
|
-
import {Chessboard} from "../../src/Chessboard.js"
|
|
22
|
-
import {FEN} from "../../src/model/Position.js"
|
|
23
|
-
|
|
24
|
-
const boardElement = document.getElementById("board")
|
|
25
|
-
const chessboard = new Chessboard(boardElement, {
|
|
26
|
-
position: FEN.start,
|
|
27
|
-
assetsUrl: "../../assets/",
|
|
28
|
-
extensions: [{class: RenderVideo}]})
|
|
29
|
-
await chessboard.recorderInit()
|
|
30
|
-
await chessboard.recorderStart()
|
|
31
|
-
await chessboard.recorderPause(500)
|
|
32
|
-
await chessboard.movePiece("e2", "e4", true)
|
|
33
|
-
await chessboard.recorderPause(500)
|
|
34
|
-
await chessboard.movePiece("e7", "e5", true)
|
|
35
|
-
await chessboard.recorderPause(1000)
|
|
36
|
-
chessboard.recorderStop().then((url) => {
|
|
37
|
-
const video = document.getElementById("videoOut")
|
|
38
|
-
video.src = url
|
|
39
|
-
document.getElementById("download-link").href = url
|
|
40
|
-
document.getElementById("download-link").style.visibility = "visible"
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
</script>
|
|
44
|
-
</body>
|
|
45
|
-
</html>
|
|
@@ -1,162 +0,0 @@
|
|
|
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
|
-
|
|
7
|
-
import {Extension, EXTENSION_POINT} from "../../model/Extension.js"
|
|
8
|
-
import {ANIMATION_EVENT_TYPE} from "../../view/PositionAnimationsQueue.js"
|
|
9
|
-
|
|
10
|
-
export class RenderVideo extends Extension {
|
|
11
|
-
|
|
12
|
-
/** @constructor */
|
|
13
|
-
constructor(chessboard, props) {
|
|
14
|
-
super(chessboard)
|
|
15
|
-
this.props = {
|
|
16
|
-
mediaType: "auto",
|
|
17
|
-
safariMode: true
|
|
18
|
-
}
|
|
19
|
-
Object.assign(this.props, props)
|
|
20
|
-
if(this.props.mediaType === "auto") {
|
|
21
|
-
if(MediaRecorder.isTypeSupported("video/mp4;codecs:h264,aac")) {
|
|
22
|
-
this.props.mediaType = "video/mp4;codecs:h264,aac"
|
|
23
|
-
} else if(MediaRecorder.isTypeSupported("video/webm;codecs=h264")) {
|
|
24
|
-
this.props.mediaType = "video/webm;codecs=H264"
|
|
25
|
-
} else if(MediaRecorder.isTypeSupported("video/webm")) {
|
|
26
|
-
this.props.mediaType = "video/webm"
|
|
27
|
-
} else if(MediaRecorder.isTypeSupported("video/mp4")) {
|
|
28
|
-
this.props.mediaType = "video/mp4"
|
|
29
|
-
}else {
|
|
30
|
-
console.error("no suitable mediaType found")
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
console.log("recorder mediaType", this.props.mediaType)
|
|
34
|
-
this.makeSpriteInline()
|
|
35
|
-
this.registerExtensionPoint(EXTENSION_POINT.animation, async (event) => {
|
|
36
|
-
if(event.type === ANIMATION_EVENT_TYPE.frame) {
|
|
37
|
-
if (this.recorder && this.recorder.state === "recording") {
|
|
38
|
-
await this.cloneImageAndRender()
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
})
|
|
42
|
-
chessboard.recorderInit = () => {
|
|
43
|
-
return new Promise((resolve) => {
|
|
44
|
-
let all = document.querySelectorAll("svg *")
|
|
45
|
-
for (let i = 0; i < all.length; i++) {
|
|
46
|
-
this.transferComputedStyle(all[i])
|
|
47
|
-
}
|
|
48
|
-
const dimensions = this.chessboard.view.svg.getBBox()
|
|
49
|
-
this.canvas = document.createElement("canvas")
|
|
50
|
-
this.canvas.style.backgroundColor = "#000"
|
|
51
|
-
this.canvas.style.display = "none"
|
|
52
|
-
this.canvas.width = dimensions.width
|
|
53
|
-
this.canvas.height = dimensions.height
|
|
54
|
-
this.context = this.canvas.getContext('2d')
|
|
55
|
-
document.body.append(this.canvas)
|
|
56
|
-
|
|
57
|
-
this.image = new Image()
|
|
58
|
-
this.image.width = this.canvas.width
|
|
59
|
-
this.image.height = this.canvas.height
|
|
60
|
-
this.image.style.position = "absolute"
|
|
61
|
-
this.image.style.visibility = "hidden"
|
|
62
|
-
document.body.append(this.image)
|
|
63
|
-
|
|
64
|
-
this.stream = this.canvas.captureStream(50)
|
|
65
|
-
this.recorder = new MediaRecorder(this.stream, {
|
|
66
|
-
mimeType: this.props.mediaType})
|
|
67
|
-
this.recorder.ondataavailable = (event) => {
|
|
68
|
-
if (event.data && event.data.size) {
|
|
69
|
-
this.recordedData.push(event.data)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
setTimeout(() => {
|
|
73
|
-
resolve()
|
|
74
|
-
}, 100)
|
|
75
|
-
})
|
|
76
|
-
}
|
|
77
|
-
chessboard.recorderStart = () => {
|
|
78
|
-
return new Promise((resolve) => {
|
|
79
|
-
if (this.recorder && this.recorder.state === "recording") {
|
|
80
|
-
console.error("recorder is running")
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
this.recordedData = []
|
|
84
|
-
this.recorder.start(100)
|
|
85
|
-
console.log("recorder", this.recorder.state)
|
|
86
|
-
resolve()
|
|
87
|
-
})
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* returns the url of the recorded media
|
|
91
|
-
*/
|
|
92
|
-
chessboard.recorderStop = () => {
|
|
93
|
-
return new Promise(async (resolve, reject) => {
|
|
94
|
-
if (!this.recorder || this.recorder.state !== "recording") {
|
|
95
|
-
reject("recorder is not recording")
|
|
96
|
-
}
|
|
97
|
-
await this.cloneImageAndRender()
|
|
98
|
-
setTimeout(() => {
|
|
99
|
-
this.recorder.stop()
|
|
100
|
-
console.log("recorder", this.recorder.state)
|
|
101
|
-
resolve(URL.createObjectURL(new Blob(this.recordedData, {type: this.props.mediaType})))
|
|
102
|
-
}, 200)
|
|
103
|
-
})
|
|
104
|
-
}
|
|
105
|
-
chessboard.recorderPause = (ms) => {
|
|
106
|
-
return new Promise(async (resolve) => {
|
|
107
|
-
await this.cloneImageAndRender()
|
|
108
|
-
const interval = setInterval(async () => {
|
|
109
|
-
await this.cloneImageAndRender()
|
|
110
|
-
}, 50)
|
|
111
|
-
setTimeout(async () => {
|
|
112
|
-
clearInterval(interval)
|
|
113
|
-
await this.cloneImageAndRender()
|
|
114
|
-
resolve()
|
|
115
|
-
}, ms)
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
async cloneImageAndRender() {
|
|
121
|
-
return new Promise((resolve) => {
|
|
122
|
-
let serialized = new XMLSerializer().serializeToString(this.chessboard.view.svg)
|
|
123
|
-
const blob = new Blob([serialized], {type: "image/svg+xml"})
|
|
124
|
-
const blobURL = URL.createObjectURL(blob)
|
|
125
|
-
// strange Safari "bug" that the content has only 300x150px
|
|
126
|
-
const image = this.props.safariMode ? this.image : new Image()
|
|
127
|
-
image.onload = () => {
|
|
128
|
-
this.context.drawImage(image, 0, 0, this.canvas.width, this.canvas.height, 0, 0, this.canvas.width, this.canvas.height)
|
|
129
|
-
resolve()
|
|
130
|
-
}
|
|
131
|
-
image.src = blobURL
|
|
132
|
-
})
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
transferComputedStyle(element) {
|
|
136
|
-
const computedStyle = getComputedStyle(element, null)
|
|
137
|
-
for (let i = 0; i < computedStyle.length; i++) {
|
|
138
|
-
const style = computedStyle[i] + ""
|
|
139
|
-
if (["fill", "stroke", "stroke-width", "font-size"].includes(style)) {
|
|
140
|
-
element.style[style] = computedStyle[style]
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
makeSpriteInline() {
|
|
146
|
-
const wrapper = document.createElement("div")
|
|
147
|
-
wrapper.style.display = "none"
|
|
148
|
-
document.body.appendChild(wrapper)
|
|
149
|
-
const svg = this.chessboard.view.svg
|
|
150
|
-
const xhr = new XMLHttpRequest()
|
|
151
|
-
xhr.open("GET", this.chessboard.view.getSpriteUrl(), true)
|
|
152
|
-
xhr.onload = function () {
|
|
153
|
-
const doc = new DOMParser().parseFromString(xhr.response, "text/xml")
|
|
154
|
-
const pieces = doc.querySelectorAll("svg>g")
|
|
155
|
-
for (const piece of pieces) {
|
|
156
|
-
svg.prepend(piece)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
xhr.send()
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
}
|