@damienmortini/three 0.1.131

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.
@@ -0,0 +1,279 @@
1
+ import { BackSide } from '../../../three/src/constants.js'
2
+ import { Mesh } from '../../../three/src/objects/Mesh.js'
3
+ import { Vector3 } from '../../../three/src/math/Vector3.js'
4
+ import { IcosahedronBufferGeometry } from '../../../three/src/geometries/IcosahedronGeometry.js'
5
+ import THREEShaderMaterial from '../material/THREEShaderMaterial.js'
6
+ import SkyShader from '../../core/shader/SkyShader.js'
7
+ import GradientNoiseShader from '../../core/shader/noise/GradientNoiseShader.js'
8
+
9
+ const skyShader = {
10
+ uniforms: {
11
+ sunPosition: new Vector3(0, 1, 0),
12
+ sunRayleigh: 1.5,
13
+ sunTurbidity: 6,
14
+ sunLuminance: 1,
15
+ sunMieCoefficient: 0.005,
16
+ sunMieDirectionalG: 0.8,
17
+ moonPosition: new Vector3(0, 1, 0),
18
+ moonRayleigh: 0,
19
+ moonTurbidity: 1.5,
20
+ moonLuminance: 1.1,
21
+ moonMieCoefficient: 0.005,
22
+ moonMieDirectionalG: 0.8,
23
+ displaySun: 1,
24
+ displayMoon: 1,
25
+ },
26
+ vertexShaderChunks: [
27
+ ['start', `
28
+ varying vec3 vWorldPosition;
29
+ `],
30
+ ['end', `
31
+ vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
32
+ vWorldPosition = worldPosition.xyz;
33
+
34
+ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
35
+ `],
36
+ ],
37
+ fragmentShaderChunks: [
38
+ ['start', `
39
+ uniform vec3 sunPosition;
40
+ uniform float sunRayleigh;
41
+ uniform float sunTurbidity;
42
+ uniform float sunLuminance;
43
+ uniform float sunMieCoefficient;
44
+ uniform float sunMieDirectionalG;
45
+ uniform float displaySun;
46
+ uniform float displayMoon;
47
+
48
+ uniform vec3 moonPosition;
49
+ uniform float moonRayleigh;
50
+ uniform float moonTurbidity;
51
+ uniform float moonLuminance;
52
+ uniform float moonMieCoefficient;
53
+ uniform float moonMieDirectionalG;
54
+
55
+ varying vec3 vWorldPosition;
56
+
57
+ ${SkyShader.computeSkyColor()}
58
+
59
+ ${GradientNoiseShader.gradientNoise3D()}
60
+
61
+ float blendScreen(float base, float blend) {
62
+ return 1.0-((1.0-base)*(1.0-blend));
63
+ }
64
+
65
+ vec3 blendScreen(vec3 base, vec3 blend) {
66
+ return vec3(blendScreen(base.r,blend.r),blendScreen(base.g,blend.g),blendScreen(base.b,blend.b));
67
+ }
68
+ `],
69
+ ['end', `
70
+ vec3 normalizedSunPosition = normalize(sunPosition);
71
+ vec3 normalizedMoonPosition = normalize(moonPosition);
72
+
73
+ vec4 skySunColor = computeSkyColor(vWorldPosition, normalizedSunPosition, ${SkyShader.SUN_ANGULAR_DIAMETER} * displaySun, sunRayleigh, sunTurbidity, sunLuminance, sunMieCoefficient, sunMieDirectionalG);
74
+ vec4 skyMoonColor = computeSkyColor(vWorldPosition, normalizedMoonPosition, ${SkyShader.MOON_ANGULAR_DIAMETER} * displayMoon, moonRayleigh, moonTurbidity, moonLuminance, moonMieCoefficient, moonMieDirectionalG);
75
+
76
+ float nightIntensity = 1. - smoothstep(-.05, 0., normalizedSunPosition.y);
77
+
78
+ float moonIntensity = max(0., -dot(normalize(sunPosition.xz), normalize(moonPosition.xz)));
79
+ // skyMoonColor *= moonIntensity;
80
+ skyMoonColor *= nightIntensity;
81
+
82
+ // Stars
83
+ vec3 rayDirection = normalize(vWorldPosition);
84
+ float starsIntensity = gradientNoise3D(rayDirection * 400.) * .5 + .5;
85
+ starsIntensity = pow(starsIntensity, 15.);
86
+ starsIntensity *= max(0., rayDirection.y);
87
+ starsIntensity *= 10.;
88
+ starsIntensity *= nightIntensity;
89
+
90
+ skyMoonColor.rgb = blendScreen(skyMoonColor.rgb, vec3(starsIntensity));
91
+
92
+ vec3 skyColor = blendScreen(skySunColor.rgb, skyMoonColor.rgb);
93
+
94
+ // gl_FragColor = vec4(skyColor, (skySunColor.a + skyMoonColor.a) / 2.);
95
+ gl_FragColor = vec4(skyColor, 1.);
96
+ `],
97
+ ],
98
+ }
99
+
100
+ export default class Sky extends Mesh {
101
+ constructor({
102
+ radius = 1,
103
+ shaders = [],
104
+ } = {}) {
105
+ super(new IcosahedronBufferGeometry(radius, 3), new THREEShaderMaterial(Object.assign({
106
+ type: 'basic',
107
+ side: BackSide,
108
+ shaders,
109
+ }, skyShader)))
110
+
111
+ this._radius = radius
112
+
113
+ this.sunInclination = Math.PI * .5
114
+ this.sunAzimuth = 0
115
+
116
+ this.moonInclination = Math.PI * .5
117
+ this.moonAzimuth = Math.PI
118
+ }
119
+
120
+ get radius() {
121
+ return this._radius
122
+ }
123
+
124
+ _updatePositionFromInclinationAzimuth(position, inclination, azimuth) {
125
+ const theta = inclination
126
+ const phi = azimuth + Math.PI * .5
127
+ position.x = this._radius * Math.cos(phi) * Math.cos(theta)
128
+ position.y = this._radius * Math.sin(theta)
129
+ position.z = this._radius * Math.sin(phi) * Math.cos(theta)
130
+ }
131
+
132
+ get displaySun() {
133
+ return this.material.displaySun === 1
134
+ }
135
+
136
+ set displaySun(value) {
137
+ this.material.displaySun = value ? 1 : 0
138
+ }
139
+
140
+ get displayMoon() {
141
+ return this.material.displayMoon === 1
142
+ }
143
+
144
+ set displayMoon(value) {
145
+ this.material.displayMoon = value ? 1 : 0
146
+ }
147
+
148
+ get sunInclination() {
149
+ return this._sunInclination
150
+ }
151
+
152
+ set sunInclination(value) {
153
+ this._sunInclination = value
154
+ this._updatePositionFromInclinationAzimuth(this.sunPosition, this.sunInclination, this.sunAzimuth)
155
+ }
156
+
157
+ get sunAzimuth() {
158
+ return this._sunAzimuth
159
+ }
160
+
161
+ set sunAzimuth(value) {
162
+ this._sunAzimuth = value
163
+ this._updatePositionFromInclinationAzimuth(this.sunPosition, this.sunInclination, this.sunAzimuth)
164
+ }
165
+
166
+ get sunPosition() {
167
+ return this.material.sunPosition
168
+ }
169
+
170
+ set sunPosition(value) {
171
+ this.material.sunPosition = value
172
+ }
173
+
174
+ get sunRayleigh() {
175
+ return this.material.sunRayleigh
176
+ }
177
+
178
+ set sunRayleigh(value) {
179
+ this.material.sunRayleigh = value
180
+ }
181
+
182
+ get sunTurbidity() {
183
+ return this.material.sunTurbidity
184
+ }
185
+
186
+ set sunTurbidity(value) {
187
+ this.material.sunTurbidity = value
188
+ }
189
+
190
+ get sunLuminance() {
191
+ return this.material.sunLuminance
192
+ }
193
+
194
+ set sunLuminance(value) {
195
+ this.material.sunLuminance = value
196
+ }
197
+
198
+ get sunMieCoefficient() {
199
+ return this.material.sunMieCoefficient
200
+ }
201
+
202
+ set sunMieCoefficient(value) {
203
+ this.material.sunMieCoefficient = value
204
+ }
205
+
206
+ get sunMieDirectionalG() {
207
+ return this.material.sunMieDirectionalG
208
+ }
209
+
210
+ set sunMieDirectionalG(value) {
211
+ this.material.sunMieDirectionalG = value
212
+ }
213
+
214
+ get moonInclination() {
215
+ return this._moonInclination
216
+ }
217
+
218
+ set moonInclination(value) {
219
+ this._moonInclination = value
220
+ this._updatePositionFromInclinationAzimuth(this.moonPosition, this.moonInclination, this.moonAzimuth)
221
+ }
222
+
223
+ get moonAzimuth() {
224
+ return this._moonAzimuth
225
+ }
226
+
227
+ set moonAzimuth(value) {
228
+ this._moonAzimuth = value
229
+ this._updatePositionFromInclinationAzimuth(this.moonPosition, this.moonInclination, this.moonAzimuth)
230
+ }
231
+
232
+ get moonPosition() {
233
+ return this.material.moonPosition
234
+ }
235
+
236
+ set moonPosition(value) {
237
+ this.material.moonPosition = value
238
+ }
239
+
240
+ get moonRayleigh() {
241
+ return this.material.moonRayleigh
242
+ }
243
+
244
+ set moonRayleigh(value) {
245
+ this.material.moonRayleigh = value
246
+ }
247
+
248
+ get moonTurbidity() {
249
+ return this.material.moonTurbidity
250
+ }
251
+
252
+ set moonTurbidity(value) {
253
+ this.material.moonTurbidity = value
254
+ }
255
+
256
+ get moonLuminance() {
257
+ return this.material.moonLuminance
258
+ }
259
+
260
+ set moonLuminance(value) {
261
+ this.material.moonLuminance = value
262
+ }
263
+
264
+ get moonMieCoefficient() {
265
+ return this.material.moonMieCoefficient
266
+ }
267
+
268
+ set moonMieCoefficient(value) {
269
+ this.material.moonMieCoefficient = value
270
+ }
271
+
272
+ get moonMieDirectionalG() {
273
+ return this.material.moonMieDirectionalG
274
+ }
275
+
276
+ set moonMieDirectionalG(value) {
277
+ this.material.moonMieDirectionalG = value
278
+ }
279
+ }
@@ -0,0 +1,96 @@
1
+ import { Object3D, Mesh, Color, PlaneGeometry, Texture, DoubleSide, LinearFilter } from '../../../three/build/three.module.js'
2
+
3
+ import THREEShaderMaterial from './THREEShaderMaterial.js'
4
+
5
+ const CACHED_IMAGES = new Map()
6
+
7
+ export default class Sprite extends Object3D {
8
+ constructor(image, { data, frame, scale = 1 } = {}) {
9
+ super()
10
+
11
+ this._data = data
12
+ this._image = image
13
+ this._scale = scale
14
+
15
+ // Optimise images decoding
16
+ let canvas = CACHED_IMAGES.get(this.image)
17
+
18
+ if (!canvas) {
19
+ canvas = document.createElement('canvas')
20
+ canvas.width = this.image.width
21
+ canvas.height = this.image.height
22
+ const context = canvas.getContext('2d')
23
+ context.drawImage(this.image, 0, 0)
24
+ CACHED_IMAGES.set(this.image, canvas)
25
+ }
26
+
27
+ this.mesh = new Mesh(new PlaneGeometry(1, 1), new THREEShaderMaterial({
28
+ type: 'basic',
29
+ transparent: true,
30
+ side: DoubleSide,
31
+ uniforms: {
32
+ diffuse: new Color(0xffffff),
33
+ map: new Texture(canvas),
34
+ },
35
+ }))
36
+ this.mesh.scale.x = canvas.width * this._scale
37
+ this.mesh.scale.y = canvas.height * this._scale
38
+ this.mesh.material.map.minFilter = LinearFilter
39
+ this.mesh.material.map.generateMipmaps = false
40
+ this.mesh.material.map.needsUpdate = true
41
+
42
+ this.add(this.mesh)
43
+
44
+ if (frame) {
45
+ this.frame = frame
46
+ }
47
+ }
48
+
49
+ get image() {
50
+ return this._image
51
+ }
52
+
53
+ get data() {
54
+ return this._data
55
+ }
56
+
57
+ set material(value) {
58
+ this.mesh.material = value
59
+ this.frame = this.frame
60
+ }
61
+
62
+ get material() {
63
+ return this.mesh.material
64
+ }
65
+
66
+ set frame(value) {
67
+ if (!this.data) {
68
+ return
69
+ }
70
+
71
+ this._frame = value
72
+
73
+ const offsetRepeat = this.mesh.material.offsetRepeat
74
+ const frameData = this.data.frames[this._frame]
75
+
76
+ offsetRepeat.z = (frameData.rotated ? frameData.frame.h : frameData.frame.w) / this.image.width
77
+ offsetRepeat.w = (frameData.rotated ? frameData.frame.w : frameData.frame.h) / this.image.height
78
+
79
+ offsetRepeat.x = frameData.frame.x / this.image.width
80
+ offsetRepeat.y = 1. - frameData.frame.y / this.image.height - offsetRepeat.w
81
+
82
+ const scale = 1 / parseFloat(this.data.meta.scale)
83
+
84
+ this.mesh.scale.x = (frameData.rotated ? frameData.frame.h : frameData.frame.w) * scale * this._scale
85
+ this.mesh.scale.y = (frameData.rotated ? frameData.frame.w : frameData.frame.h) * scale * this._scale
86
+
87
+ this.mesh.position.x = (-(frameData.sourceSize.w - frameData.frame.w) * .5 + frameData.spriteSourceSize.x + frameData.sourceSize.w * (.5 - frameData.pivot.x)) * scale * this._scale
88
+ this.mesh.position.y = ((frameData.sourceSize.h - frameData.frame.h) * .5 - frameData.spriteSourceSize.y - frameData.sourceSize.h * (.5 - frameData.pivot.y)) * scale * this._scale
89
+
90
+ this.mesh.rotation.z = frameData.rotated ? Math.PI * .5 : 0
91
+ }
92
+
93
+ get frame() {
94
+ return this._frame
95
+ }
96
+ }
@@ -0,0 +1,103 @@
1
+
2
+ import THREESprite from './THREESprite.js'
3
+
4
+ import Signal from '../../core/util/Signal'
5
+ import Ticker from '../../core/util/Ticker'
6
+
7
+ const SPRITESHEETS = new Map()
8
+
9
+ export default class THREESpriteAnimation extends THREESprite {
10
+ constructor(image, data, animation, {
11
+ speed = 1,
12
+ fps = 25,
13
+ loop = true,
14
+ reverse = false,
15
+ scale = 1,
16
+ autoplay = true,
17
+ } = {}) {
18
+ let animations = SPRITESHEETS.get(data)
19
+ if (!animations) {
20
+ animations = new Map()
21
+ for (const key in data.frames) {
22
+ const match = /(.*?)([0-9]+)[$\.]/.exec(key)
23
+ const animationName = match[1]
24
+ let frames = animations.get(animationName)
25
+ if (!frames) {
26
+ frames = []
27
+ animations.set(animationName, frames)
28
+ }
29
+ const position = parseInt(match[2])
30
+ frames[position - 1] = key
31
+ }
32
+ SPRITESHEETS.set(data, animations)
33
+ }
34
+
35
+ super(image, {
36
+ data,
37
+ frame: animations.get(animation)[0],
38
+ scale,
39
+ })
40
+
41
+ this.onAnimationEnd = new Signal()
42
+
43
+ this._progress = 0
44
+ this._animations = animations
45
+
46
+ this.loop = loop
47
+ this.reverse = reverse
48
+ this.speed = speed
49
+ this.fps = fps
50
+ this.animation = animation
51
+
52
+ if (autoplay) {
53
+ this.play()
54
+ }
55
+ }
56
+
57
+ set animation(value) {
58
+ if (this._animation === value) {
59
+ return
60
+ }
61
+ this._animation = value
62
+
63
+ this.update()
64
+ }
65
+
66
+ get animation() {
67
+ return this._animation
68
+ }
69
+
70
+ play() {
71
+ Ticker.add(this._updateBound = this._updateBound || this.update.bind(this))
72
+ }
73
+
74
+ stop() {
75
+ Ticker.delete(this._updateBound)
76
+ }
77
+
78
+ set progress(value) {
79
+ if (this._progress === value) {
80
+ return
81
+ }
82
+ const previousProgress = this._progress
83
+ this._progress = value
84
+ if (this.loop) {
85
+ this._progress = ((this._progress % 1) + 1) % 1
86
+ } else {
87
+ this._progress = Math.min(Math.max(this._progress, 0), 1)
88
+ if (previousProgress !== this._progress && (this._progress === 1 && !this.reverse || this._progress === 0 && this.reverse)) {
89
+ this.onAnimationEnd.dispatch()
90
+ }
91
+ }
92
+ }
93
+
94
+ get progress() {
95
+ return this._progress
96
+ }
97
+
98
+ update() {
99
+ const frames = this._animations.get(this.animation)
100
+ this.progress += (this.speed * (this.fps / 60) * Ticker.timeScale / frames.length) * (this.reverse ? -1 : 1)
101
+ this.frame = frames[Math.round(this._progress * (frames.length - 1))]
102
+ }
103
+ }
@@ -0,0 +1,240 @@
1
+ import { Object3D } from '../../../three/src/core/Object3D.js'
2
+ import { Mesh } from '../../../three/src/objects/Mesh.js'
3
+ import { PlaneGeometry } from '../../../three/src/geometries/PlaneGeometry.js'
4
+ import { Texture } from '../../../three/src/textures/Texture.js'
5
+ import { LinearFilter } from '../../../three/src/constants.js'
6
+
7
+ import THREEShaderMaterial from '../material/THREEShaderMaterial.js'
8
+
9
+ export default class THREEText extends Object3D {
10
+ constructor({
11
+ textContent = '',
12
+ font = '10px sans-serif',
13
+ fillStyle = 'black',
14
+ textAlign = 'start',
15
+ shadowColor = 'rgba(0, 0, 0 ,0)',
16
+ shadowBlur = 0,
17
+ shadowOffsetX = 0,
18
+ shadowOffsetY = 0,
19
+ scale = 1,
20
+ maxWidth = Infinity,
21
+ geometry = new PlaneGeometry(1, 1),
22
+ material = new THREEShaderMaterial({
23
+ type: 'basic',
24
+ transparent: true,
25
+ }),
26
+ } = {}) {
27
+ super()
28
+
29
+ this._scale = scale
30
+
31
+ this._canvas = document.createElement('canvas')
32
+ this._context = this._canvas.getContext('2d')
33
+
34
+ this._texture = new Texture(this._canvas)
35
+
36
+ material.map = this._texture
37
+
38
+ this.textContent = textContent
39
+ this.font = font
40
+ this.fillStyle = fillStyle
41
+ this.textAlign = textAlign
42
+ this.maxWidth = maxWidth
43
+ this.shadowColor = shadowColor
44
+ this.shadowBlur = shadowBlur
45
+ this.shadowOffsetX = shadowOffsetX
46
+ this.shadowOffsetY = shadowOffsetY
47
+
48
+ this._mesh = new Mesh(geometry, material)
49
+ this.add(this._mesh)
50
+
51
+ this._update()
52
+ }
53
+
54
+ _updateContextProperties() {
55
+ this._context.font = this.font
56
+ this._context.fillStyle = this.fillStyle
57
+ this._context.shadowColor = this.shadowColor
58
+ this._context.shadowBlur = this.shadowBlur
59
+ this._context.shadowOffsetX = this.shadowOffsetX
60
+ this._context.shadowOffsetY = this.shadowOffsetY
61
+ this._context.textBaseline = 'top'
62
+ }
63
+
64
+ _update() {
65
+ if (!this._mesh) {
66
+ return
67
+ }
68
+
69
+ this._updateContextProperties()
70
+
71
+ const shadowOffsetX = this.shadowOffsetX - this.shadowBlur
72
+ const shadowOffsetY = this.shadowOffsetY - this.shadowBlur
73
+
74
+ const words = this.textContent.split(' ')
75
+
76
+ const spaceWidth = this._context.measureText(' ').width
77
+ const wordsWidth = new Map()
78
+ const lines = [{
79
+ textContent: '',
80
+ width: 0,
81
+ }]
82
+ for (const word of words) {
83
+ if (!wordsWidth.get(word)) {
84
+ wordsWidth.set(word, this._context.measureText(word).width)
85
+ }
86
+ }
87
+
88
+ let width = 0
89
+ let lineNumber = 0
90
+ for (const word of words) {
91
+ const newWidth = lines[lineNumber].width + wordsWidth.get(word)
92
+
93
+ if (newWidth > this.maxWidth) {
94
+ lineNumber++
95
+ lines[lineNumber] = {
96
+ textContent: word,
97
+ width: wordsWidth.get(word),
98
+ }
99
+ } else {
100
+ if (lines[lineNumber].textContent !== '') {
101
+ lines[lineNumber].textContent += ' '
102
+ }
103
+ lines[lineNumber].textContent += word
104
+ lines[lineNumber].width += spaceWidth + wordsWidth.get(word)
105
+ }
106
+ width = Math.max(width, lines[lineNumber].width)
107
+ }
108
+
109
+ width += this.shadowBlur * 2 + Math.abs(this.shadowOffsetX)
110
+
111
+ const lineHeight = parseFloat(/\b(\d*)px/.exec(this._context.font)[1])
112
+ let height = lineHeight
113
+ height *= lines.length
114
+ height += this.shadowBlur * 2 + Math.abs(this.shadowOffsetY)
115
+
116
+ if (this._canvas.width !== width || this._canvas.height !== height) {
117
+ this._canvas.width = width
118
+ this._canvas.height = height
119
+ this._updateContextProperties()
120
+ }
121
+
122
+ this._mesh.position.y = -shadowOffsetY * .5 * this._scale
123
+
124
+ if (this.textAlign === 'start' || this.textAlign === 'left') {
125
+ this._mesh.position.x = (this._canvas.width * .5 + Math.min(0, shadowOffsetX)) * this._scale
126
+ } else if (this.textAlign === 'end' || this.textAlign === 'right') {
127
+ this._mesh.position.x = (-this._canvas.width * .5 + Math.max(0, shadowOffsetX)) * this._scale
128
+ } else {
129
+ this._mesh.position.x = shadowOffsetX * .5 * this._scale
130
+ }
131
+ this._mesh.scale.x = this._canvas.width * this._scale
132
+ this._mesh.scale.y = this._canvas.height * this._scale
133
+ this._context.globalAlpha = 1 / 255
134
+ this._context.fillRect(0, 0, width, height)
135
+ this._context.globalAlpha = 1
136
+ for (const [i, line] of lines.entries()) {
137
+ let offsetX
138
+ switch (this.textAlign) {
139
+ case 'start':
140
+ case 'left':
141
+ offsetX = 0
142
+ break
143
+ case 'center':
144
+ offsetX = (width - line.width) * .5
145
+ break
146
+ case 'end':
147
+ case 'right':
148
+ offsetX = width - line.width
149
+ break
150
+ }
151
+ this._context.fillText(line.textContent, offsetX + (shadowOffsetX < 0 ? Math.abs(shadowOffsetX) : 0), (shadowOffsetY < 0 ? Math.abs(shadowOffsetY) : 0) + lineHeight * i)
152
+ }
153
+ this._texture.needsUpdate = true
154
+ }
155
+
156
+ get material() {
157
+ return this._mesh.material
158
+ }
159
+
160
+ set textContent(value) {
161
+ this._textContent = value
162
+ this._update()
163
+ }
164
+
165
+ get textContent() {
166
+ return this._textContent
167
+ }
168
+
169
+ set font(value) {
170
+ this._context.font = this._font = value
171
+ this._update()
172
+ }
173
+
174
+ get font() {
175
+ return this._font
176
+ }
177
+
178
+ set fillStyle(value) {
179
+ this._context.fillStyle = this._fillStyle = value
180
+ this._update()
181
+ }
182
+
183
+ get fillStyle() {
184
+ return this._fillStyle
185
+ }
186
+
187
+ set textAlign(value) {
188
+ this._textAlign = value
189
+ this._update()
190
+ }
191
+
192
+ get textAlign() {
193
+ return this._textAlign
194
+ }
195
+
196
+ set maxWidth(value) {
197
+ this._maxWidth = value
198
+ this._update()
199
+ }
200
+
201
+ get maxWidth() {
202
+ return this._maxWidth
203
+ }
204
+
205
+ set shadowColor(value) {
206
+ this._context.shadowColor = this._shadowColor = value
207
+ this._update()
208
+ }
209
+
210
+ get shadowColor() {
211
+ return this._shadowColor
212
+ }
213
+
214
+ set shadowBlur(value) {
215
+ this._context.shadowBlur = this._shadowBlur = value
216
+ this._update()
217
+ }
218
+
219
+ get shadowBlur() {
220
+ return this._shadowBlur
221
+ }
222
+
223
+ set shadowOffsetX(value) {
224
+ this._context.shadowOffsetX = this._shadowOffsetX = value
225
+ this._update()
226
+ }
227
+
228
+ get shadowOffsetX() {
229
+ return this._shadowOffsetX
230
+ }
231
+
232
+ set shadowOffsetY(value) {
233
+ this._context.shadowOffsetY = this._shadowOffsetY = value
234
+ this._update()
235
+ }
236
+
237
+ get shadowOffsetY() {
238
+ return this._shadowOffsetY
239
+ }
240
+ }