@houstonp/rubiks-cube 1.4.1 → 1.5.1

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
@@ -1,6 +1,6 @@
1
1
  # Rubiks Cube Web Component
2
2
 
3
- This package is a rubiks cube web component built with threejs. Camera animation smoothing is done with the tweenjs package.
3
+ This package is a rubiks cube web component built with threejs. Camera animation smoothing is done with the gsap package.
4
4
 
5
5
  ![cube](cube.png)
6
6
 
@@ -21,7 +21,7 @@ import '@houstonp/rubiks-cube';
21
21
  <meta charset="utf-8" />
22
22
  </head>
23
23
  <body>
24
- <rubiks-cube animation-speed="1000" animation-style="exponential" piece-gap="1.04" camera-speed="100"> </rubiks-cube>
24
+ <rubiks-cube animation-speed-ms="1000" animation-style="exponential" piece-gap="1.04" camera-speed="100"> </rubiks-cube>
25
25
  <script type="module" src="index.js"></script>
26
26
  </body>
27
27
  </html>
@@ -29,12 +29,16 @@ import '@houstonp/rubiks-cube';
29
29
 
30
30
  ## component attributes
31
31
 
32
- | attribute | accepted values | Description |
33
- | --------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
34
- | animation-speed | integer greater than or equal to 0 | sets the speed of the animations in milliseconds |
35
- | animation-style | "exponetial", "next", "fixed", "match" | fixed: fixed animation lengths, next: skips to next animation, exponential: speeds up successive animations, match: matches the speed the frequency of events |
36
- | piece-gap | greater than 1 | sets the gap between rubiks cube pieces |
37
- | camera-speed | greater than or equal to 0 | sets the speed of camera animations in milliseconds |
32
+ | attribute | accepted values | Description |
33
+ | ---------------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
34
+ | animation-speed-ms | integer greater than or equal to 0 | sets the duration of the animations in milliseconds |
35
+ | animation-style | "exponetial", "next", "fixed", "match" | fixed: fixed animation lengths, next: skips to next animation, exponential: speeds up successive animations, match: matches the speed the frequency of events |
36
+ | piece-gap | greater than 1 | sets the gap between rubiks cube pieces |
37
+ | camera-speed-ms | greater than or equal to 0 | sets the duration of camera animations in milliseconds |
38
+ | camera-radius | greater than or equal to 4 | sets the camera radius |
39
+ | camera-peek-angle-horizontal | decimal between 0 and 1 | sets the horizontal peek angle |
40
+ | camera-peek-angle-vertical | decimal between 0 and 1 | sets the vertical peek angle |
41
+ | camera-field-of-view | integer between 40 and 100 | sets the fielf of view of the camera |
38
42
 
39
43
  ## state of the component
40
44
 
@@ -156,12 +160,12 @@ var event = new CustomEvent('action', {
156
160
 
157
161
  action IDs for camera actions are as follows
158
162
 
159
- - `peek-right` - Camera is moved to the right of the cube so that the right face is visible
160
- - `peek-left` - Camera is moved to the left of the cube so that the left face is visible
161
- - `peek-top` - Camera is moved above the cube so that the top face is visible
162
- - `peek-bottom` - Camera is moved below the cube so that the bottom face is visible
163
- - `peek-toggle-horizontal` - Camera is moved to the opposite side of the cube in the horizontal plane
164
- - `peek-toggle-vertical` - Camera is moved to the opposite side of the cube in the vertical plane
163
+ - `peek-right` - Camera is moved to the right of the cube so that the right face is visible
164
+ - `peek-left` - Camera is moved to the left of the cube so that the left face is visible
165
+ - `peek-top` - Camera is moved above the cube so that the top face is visible
166
+ - `peek-bottom` - Camera is moved below the cube so that the bottom face is visible
167
+ - `peek-toggle-horizontal` - Camera is moved to the opposite side of the cube in the horizontal plane
168
+ - `peek-toggle-vertical` - Camera is moved to the opposite side of the cube in the vertical plane
165
169
 
166
170
  #### Example
167
171
 
@@ -183,15 +187,15 @@ cube.dispatchEvent(event);
183
187
 
184
188
  actionIDs for action type "rotation" are as follows
185
189
 
186
- - 'x',
187
- - 'x2',
188
- - "x'",
189
- - 'y',
190
- - 'y2',
191
- - "y'",
192
- - 'z',
193
- - 'z2',
194
- - "z'",
190
+ - 'x',
191
+ - 'x2',
192
+ - "x'",
193
+ - 'y',
194
+ - 'y2',
195
+ - "y'",
196
+ - 'z',
197
+ - 'z2',
198
+ - "z'",
195
199
 
196
200
  #### Example
197
201
 
@@ -225,25 +229,25 @@ cube.dispatchEvent(event);
225
229
 
226
230
  actionIDs for action type "movement" are as follows
227
231
 
228
- - 'R',
229
- - 'R2',
230
- - "R'",
231
- - 'L',
232
- - 'L2',
233
- - "L'",
234
- - 'U',
235
- - 'U2',
236
- - "U'",
237
- - 'D',
238
- - 'D2',
239
- - "D'",
240
- - 'F',
241
- - 'F2',
242
- - "F'",
243
- - 'B',
244
- - 'B2',
245
- - "B'",
246
- - etc...
232
+ - 'R',
233
+ - 'R2',
234
+ - "R'",
235
+ - 'L',
236
+ - 'L2',
237
+ - "L'",
238
+ - 'U',
239
+ - 'U2',
240
+ - "U'",
241
+ - 'D',
242
+ - 'D2',
243
+ - "D'",
244
+ - 'F',
245
+ - 'F2',
246
+ - "F'",
247
+ - 'B',
248
+ - 'B2',
249
+ - "B'",
250
+ - etc...
247
251
 
248
252
  #### Example
249
253
 
package/index.js CHANGED
@@ -1,54 +1,114 @@
1
- import { Scene, WebGLRenderer, PerspectiveCamera, AmbientLight, DirectionalLight } from 'three';
2
- import { Tween, Group, Easing } from '@tweenjs/tween.js';
1
+ import { Scene, WebGLRenderer, PerspectiveCamera, AmbientLight, DirectionalLight, Spherical } from 'three';
3
2
  import { OrbitControls } from 'three/examples/jsm/Addons.js';
4
3
  import Cube from './src/cube/cube';
5
4
  import getRotationDetailsFromNotation from './src/utils/rotation';
6
5
  import { debounce } from './src/utils/debouncer';
6
+ import gsap from 'gsap';
7
7
 
8
- const defaultAnimationSpeed = 100;
9
- const defaultCameraSpeed = 100;
10
- const defaultAnimationStyle = 'fixed';
11
- const defaultGap = 1.04;
12
- const minimumGap = 1;
8
+ /** @typedef {{ animationStyle: "exponential" | "next" | "fixed" | "match", animationSpeedMs: number, pieceGap: number, cameraSpeedMs: number, cameraRadius: number, cameraPeekAngleHorizontal: number, cameraPeekAngleVertical: number, cameraFieldOfView: number }} Settings */
9
+ /** @type {Settings} */
10
+ const defaultSettings = {
11
+ animationSpeedMs: 100,
12
+ animationStyle: 'fixed',
13
+ pieceGap: 1.04,
14
+ cameraSpeedMs: 100,
15
+ cameraRadius: 5,
16
+ cameraPeekAngleHorizontal: 0.6,
17
+ cameraPeekAngleVertical: 0.6,
18
+ cameraFieldOfView: 75,
19
+ };
20
+ const minGap = 1;
21
+ const minRadius = 4;
22
+ const minFieldOfView = 30;
23
+ const maxFieldOfView = 100;
24
+ const maxAzimuthAngle = (5 * Math.PI) / 16;
25
+ const polarAngleOffset = Math.PI / 2;
26
+ const maxPolarAngle = (5 * Math.PI) / 16;
13
27
 
14
28
  class RubiksCube extends HTMLElement {
15
29
  constructor() {
16
30
  super();
17
- /** @type {number} */
18
- this.animationSpeed = defaultAnimationSpeed;
19
31
  this.attachShadow({ mode: 'open' });
20
32
  this.shadowRoot.innerHTML = `<canvas id="cube-canvas" style="display:block;"></canvas>`;
21
33
  this.canvas = this.shadowRoot.getElementById('cube-canvas');
22
- /** @type {{animationStyle: "exponential" | "next" | "fixed" | "match", animationSpeed: number, gap: number, cameraSpeed: number}} */
34
+ /** @type {Settings} */
23
35
  this.settings = {
24
- animationSpeed: this.getAttribute('animation-speed') || defaultAnimationSpeed,
25
- animationStyle: this.getAttribute('animation-style') || defaultAnimationStyle,
26
- gap: this.getAttribute('piece-gap') || defaultGap,
27
- cameraSpeed: this.getAttribute('camera-speed') || defaultCameraSpeed,
36
+ animationSpeedMs: this.getAttribute('animation-speed-ms') || defaultSettings.animationSpeedMs,
37
+ animationStyle: this.getAttribute('animation-style') || defaultSettings.animationStyle,
38
+ pieceGap: this.getAttribute('piece-gap') || defaultSettings.pieceGap,
39
+ cameraSpeedMs: this.getAttribute('camera-speed-ms') || defaultSettings.cameraSpeedMs,
40
+ cameraRadius: this.getAttribute('camera-radius') || defaultSettings.cameraRadius,
41
+ cameraPeekAngleHorizontal: this.getAttribute('camera-peek-angle-horizontal') || defaultSettings.cameraPeekAngleHorizontal,
42
+ cameraPeekAngleVertical: this.getAttribute('camera-peek-angle-vertical') || defaultSettings.cameraPeekAngleVertical,
43
+ cameraFieldOfView: this.getAttribute('camera-field-of-view') || defaultSettings.cameraFieldOfView,
28
44
  };
29
45
  }
30
46
 
31
47
  static get observedAttributes() {
32
- return ['animation-style', 'animation-speed', 'piece-gap', 'camera-speed'];
48
+ return [
49
+ 'animation-style',
50
+ 'animation-speed-ms',
51
+ 'piece-gap',
52
+ 'camera-speed-ms',
53
+ 'camera-radius',
54
+ 'camera-peek-angle-horizontal',
55
+ 'camera-peek-angle-vertical',
56
+ 'camera-field-of-view',
57
+ ];
33
58
  }
34
59
 
35
60
  attributeChangedCallback(name, oldVal, newVal) {
36
61
  if (name === 'animation-style') {
37
62
  this.settings.animationStyle = newVal;
38
63
  }
39
- if (name === 'animation-speed') {
64
+ if (name === 'animation-speed-ms') {
40
65
  var speed = Number(newVal);
41
- this.settings.animationSpeed = speed > 0 ? speed : 0;
66
+ this.settings.animationSpeedMs = speed > 0 ? speed : 0;
42
67
  }
43
68
  if (name === 'piece-gap') {
44
69
  var gap = Number(newVal);
45
- this.settings.gap = gap < minimumGap ? minimumGap : gap;
70
+ this.settings.pieceGap = gap < minGap ? minGap : gap;
46
71
  }
47
- if (name === 'camera-speed') {
72
+ if (name === 'camera-speed-ms') {
48
73
  var speed = Number(newVal);
49
- this.settings.cameraSpeed = speed > 0 ? speed : 0;
74
+ this.settings.cameraSpeedMs = speed > 0 ? speed : 0;
75
+ }
76
+ if (name === 'camera-radius') {
77
+ var radius = Number(newVal);
78
+ this.settings.cameraRadius = radius < minRadius ? minRadius : radius;
79
+ if (oldVal !== newVal && oldVal !== null) {
80
+ this.dispatchEvent(new CustomEvent('cameraSettingsChanged'));
81
+ }
82
+ }
83
+ if (name === 'camera-peek-angle-horizontal') {
84
+ var angle = Number(newVal);
85
+ angle = angle > 0 ? angle : 0;
86
+ angle = angle < 1 ? angle : 1;
87
+ this.settings.cameraPeekAngleHorizontal = angle > 0 ? angle : 0;
88
+ if (oldVal !== newVal && oldVal !== null) {
89
+ this.dispatchEvent(new CustomEvent('cameraSettingsChanged'));
90
+ }
91
+ }
92
+ if (name === 'camera-peek-angle-vertical') {
93
+ var angle = Number(newVal);
94
+ angle = angle > 0 ? angle : 0;
95
+ angle = angle < 1 ? angle : 1;
96
+ this.settings.cameraPeekAngleVertical = angle;
97
+ if (oldVal !== newVal && oldVal !== null) {
98
+ this.dispatchEvent(new CustomEvent('cameraSettingsChanged'));
99
+ }
100
+ }
101
+ if (name == 'camera-field-of-view') {
102
+ var fov = Number(newVal);
103
+ fov = fov > minFieldOfView ? fov : minFieldOfView;
104
+ fov = fov < maxFieldOfView ? fov : maxFieldOfView;
105
+ this.settings.cameraFieldOfView = fov;
106
+ if (oldVal !== newVal && oldVal !== null) {
107
+ this.dispatchEvent(new CustomEvent('cameraFieldOfViewChanged'));
108
+ }
50
109
  }
51
110
  }
111
+
52
112
  connectedCallback() {
53
113
  this.init();
54
114
  }
@@ -77,22 +137,21 @@ class RubiksCube extends HTMLElement {
77
137
  ).observe(this);
78
138
 
79
139
  // add camera
80
- const camera = new PerspectiveCamera(75, this.clientWidth / this.clientHeight, 0.1, 1000);
81
- /** @type {{Up: boolean, Right: boolean, UpDistance: number, RightDistance: number}} */
82
- const cameraState = { Up: true, Right: true, UpDistance: 2.5, RightDistance: 2.5 };
83
- camera.position.z = 4;
84
- camera.position.y = 3;
85
- camera.position.x = 0;
140
+ const camera = new PerspectiveCamera(this.settings.cameraFieldOfView, this.clientWidth / this.clientHeight, 1, 2000);
141
+ const cameraSpherical = new Spherical(50, (3 * Math.PI) / 8, -Math.PI / 4);
142
+ camera.position.setFromSpherical(cameraSpherical);
143
+ /** @type {{ Up: boolean, Right: boolean }} */
144
+ const cameraState = { Up: true, Right: true };
86
145
 
87
146
  // add orbit controls for camera
88
147
  const controls = new OrbitControls(camera, renderer.domElement);
89
148
  controls.enableZoom = false;
90
149
  controls.enablePan = false;
91
150
  controls.enableDamping = true;
92
- controls.maxAzimuthAngle = Math.PI / 4;
93
- controls.minAzimuthAngle = -Math.PI / 4;
94
- controls.maxPolarAngle = (3 * Math.PI) / 4;
95
- controls.minPolarAngle = Math.PI / 4;
151
+ controls.maxAzimuthAngle = maxAzimuthAngle;
152
+ controls.minAzimuthAngle = -maxAzimuthAngle;
153
+ controls.maxPolarAngle = polarAngleOffset + maxPolarAngle;
154
+ controls.minPolarAngle = polarAngleOffset - maxPolarAngle;
96
155
 
97
156
  // add lighting to scene
98
157
  const ambientLight = new AmbientLight('white', 0.5);
@@ -110,10 +169,6 @@ class RubiksCube extends HTMLElement {
110
169
  const cube = new Cube(this.settings);
111
170
  scene.add(cube.group, cube.rotationGroup);
112
171
 
113
- // initial camera animation
114
- const cameraAnimationGroup = new Group();
115
- cameraAnimationGroup.add(new Tween(camera.position).to({ x: 2.5, y: 2.5, z: 4 }, 1000).easing(Easing.Cubic.InOut).start());
116
-
117
172
  const sendState = (eventId) => {
118
173
  const event = new CustomEvent('state', { detail: { eventId, state: cube.currentState } });
119
174
  this.dispatchEvent(event);
@@ -121,7 +176,6 @@ class RubiksCube extends HTMLElement {
121
176
 
122
177
  // animation loop
123
178
  function animate() {
124
- cameraAnimationGroup.update();
125
179
  controls.update();
126
180
 
127
181
  var eventId = cube.update();
@@ -141,7 +195,8 @@ class RubiksCube extends HTMLElement {
141
195
  /** @type {{eventId: string, action: {type: "movement" | "camera" | "rotation", actionId: string }}} move */
142
196
  var move = e.detail.move;
143
197
  if (move.action.type === 'camera') {
144
- handleCameraAction(move.action.actionId);
198
+ updateCameraState(move.action.actionId);
199
+ updateCameraPosition();
145
200
  return;
146
201
  }
147
202
  if (move.action.type === 'movement' || move.action.type === 'rotation') {
@@ -162,9 +217,9 @@ class RubiksCube extends HTMLElement {
162
217
  };
163
218
 
164
219
  /**
165
- * @param {'peek-toggle-horizontal' | 'peek-toggle-vertical' | 'peek-right' | 'peek-left' | 'peek-up' | 'peek-down'} actionId
220
+ * @param {'peek-toggle-horizontal' | 'peek-toggle-vertical' | 'peek-right' | 'peek-left' | 'peek-up' | 'peek-down' } actionId
166
221
  */
167
- const handleCameraAction = (actionId) => {
222
+ const updateCameraState = (actionId) => {
168
223
  if (actionId === 'peek-toggle-horizontal') {
169
224
  cameraState.Right = !cameraState.Right;
170
225
  } else if (actionId === 'peek-toggle-vertical') {
@@ -178,19 +233,42 @@ class RubiksCube extends HTMLElement {
178
233
  } else if (actionId === 'peek-down') {
179
234
  cameraState.Up = false;
180
235
  }
181
- cameraAnimationGroup.add(
182
- new Tween(camera.position)
183
- .to(
184
- {
185
- x: cameraState.Right ? cameraState.RightDistance : -cameraState.RightDistance,
186
- y: cameraState.Up ? cameraState.UpDistance : -cameraState.UpDistance,
187
- z: 4,
188
- },
189
- this.settings.cameraSpeed,
190
- )
191
- .start(),
192
- );
193
236
  };
237
+
238
+ /**
239
+ * @param {number | null} cameraSpeedMs
240
+ * @param {gsap.EaseString | gsap.EaseFunction | undefined} ease
241
+ */
242
+ const updateCameraPosition = (cameraSpeedMs = this.settings.cameraSpeedMs, ease = 'none') => {
243
+ cameraSpeedMs = cameraSpeedMs ? cameraSpeedMs : this.settings.cameraSpeedMs;
244
+ var phi = polarAngleOffset + (cameraState.Up ? -this.settings.cameraPeekAngleVertical : this.settings.cameraPeekAngleVertical) * maxPolarAngle;
245
+ var theta = (cameraState.Right ? this.settings.cameraPeekAngleHorizontal : -this.settings.cameraPeekAngleHorizontal) * maxAzimuthAngle;
246
+ const startSpherical = new Spherical().setFromVector3(camera.position);
247
+ const targetSpherical = new Spherical(this.settings.cameraRadius, phi, theta);
248
+ gsap.to(startSpherical, {
249
+ radius: targetSpherical.radius,
250
+ theta: targetSpherical.theta,
251
+ phi: targetSpherical.phi,
252
+ duration: cameraSpeedMs / 1000,
253
+ ease: ease,
254
+ onUpdate: function () {
255
+ camera.position.setFromSpherical(startSpherical);
256
+ camera.lookAt(cube.group.position);
257
+ controls.update();
258
+ },
259
+ });
260
+ };
261
+
262
+ this.addEventListener('cameraSettingsChanged', () => {
263
+ updateCameraPosition(); // animate settings changes
264
+ });
265
+
266
+ this.addEventListener('cameraFieldOfViewChanged', () => {
267
+ camera.fov = this.settings.cameraFieldOfView;
268
+ camera.updateProjectionMatrix();
269
+ });
270
+
271
+ updateCameraPosition(1000, 'none'); // initial animation
194
272
  }
195
273
  }
196
274
  customElements.define('rubiks-cube', RubiksCube);
package/package.json CHANGED
@@ -1,15 +1,14 @@
1
1
  {
2
2
  "name": "@houstonp/rubiks-cube",
3
- "version": "1.4.1",
3
+ "version": "1.5.1",
4
4
  "description": "Rubiks Cube Web Component built with threejs",
5
5
  "main": "index.js",
6
6
  "author": "Houston Pearse",
7
7
  "license": "MIT",
8
8
  "dependencies": {
9
- "@tweenjs/tween.js": "^25.0.0",
9
+ "gsap": "^3.14.2",
10
10
  "three": "^0.167.1"
11
11
  },
12
- "devDependencies": {},
13
12
  "repository": {
14
13
  "type": "git",
15
14
  "url": "git+https://github.com/houstonpearse/rubiks-cube.git"
package/src/cube/cube.js CHANGED
@@ -5,10 +5,10 @@ import { CubeRotation } from './cubeRotation';
5
5
 
6
6
  export default class Cube {
7
7
  /**
8
- * @param {{style: "exponential" | "next" | "fixed", speed: number, gap: number}} settings
8
+ * @param {import('../..').Settings} settings
9
9
  */
10
10
  constructor(settings) {
11
- /** @type {{animationStyle: "match" | "exponential" | "next" | "fixed", animationSpeed: number, gap: number}} */
11
+ /** @type {import('../..').Settings} */
12
12
  this.settings = settings;
13
13
  /** @type {Group} */
14
14
  this.group = this.createCubeGroup();
@@ -45,7 +45,11 @@ export default class Cube {
45
45
 
46
46
  for (const piece of createCubeState()) {
47
47
  var pieceGroup = piece.group;
48
- pieceGroup.position.set(piece.position.x * this.settings.gap, piece.position.y * this.settings.gap, piece.position.z * this.settings.gap);
48
+ pieceGroup.position.set(
49
+ piece.position.x * this.settings.pieceGap,
50
+ piece.position.y * this.settings.pieceGap,
51
+ piece.position.z * this.settings.pieceGap,
52
+ );
49
53
  pieceGroup.rotation.set(piece.rotation.x, piece.rotation.y, piece.rotation.z);
50
54
  pieceGroup.userData = {
51
55
  position: Object.assign({}, piece.position),
@@ -61,11 +65,11 @@ export default class Cube {
61
65
 
62
66
  /**
63
67
  * update the cube and continue any rotations
64
- * @returns {{ up: string[][], down: string[][], front: string[][], back: string[][], left: string[][], right: string[][] } | undefined }
68
+ * @returns {string}
65
69
  */
66
70
  update() {
67
71
  if (this.currentRotation === undefined) {
68
- if (this._lastGap !== this.settings.gap) {
72
+ if (this._lastGap !== this.settings.pieceGap) {
69
73
  this.updateGap();
70
74
  }
71
75
  this.currentRotation = this.rotationQueue.shift();
@@ -100,9 +104,9 @@ export default class Cube {
100
104
  if (this.currentRotation === undefined) {
101
105
  this.group.children.forEach((piece) => {
102
106
  var { x, y, z } = piece.userData.position;
103
- piece.position.set(x * this.settings.gap, y * this.settings.gap, z * this.settings.gap);
107
+ piece.position.set(x * this.settings.pieceGap, y * this.settings.pieceGap, z * this.settings.pieceGap);
104
108
  });
105
- this._lastGap = this.settings.gap;
109
+ this._lastGap = this.settings.pieceGap;
106
110
  }
107
111
  }
108
112
 
@@ -118,15 +122,15 @@ export default class Cube {
118
122
  */
119
123
  getRotationSpeed() {
120
124
  if (this.settings.animationStyle === 'exponential') {
121
- return this.settings.animationSpeed / 2 ** this.rotationQueue.length;
125
+ return this.settings.animationSpeedMs / 2 ** this.rotationQueue.length;
122
126
  }
123
127
  if (this.settings.animationStyle === 'next') {
124
- return this.rotationQueue.length > 0 ? 0 : this.settings.animationSpeed;
128
+ return this.rotationQueue.length > 0 ? 0 : this.settings.animationSpeedMs;
125
129
  }
126
130
  if (this.settings.animationStyle === 'match') {
127
131
  if (this.rotationQueue.length > 0) {
128
132
  var lastTimeStamp = this.currentRotation.timestampMs;
129
- var minGap = this._matchSpeed ?? this.settings.animationSpeed;
133
+ var minGap = this._matchSpeed ?? this.settings.animationSpeedMs;
130
134
  for (var i = 0; i < this.rotationQueue.length; i++) {
131
135
  var gap = this.rotationQueue[i].timestampMs - lastTimeStamp;
132
136
  if (gap < minGap) {
@@ -138,12 +142,12 @@ export default class Cube {
138
142
  if (this._matchSpeed !== undefined) {
139
143
  return this._matchSpeed;
140
144
  }
141
- return this.settings.animationSpeed;
145
+ return this.settings.animationSpeedMs;
142
146
  }
143
147
  if (this.settings.animationStyle === 'fixed') {
144
- return this.settings.animationSpeed;
148
+ return this.settings.animationSpeedMs;
145
149
  }
146
- return this.settings.animationSpeed;
150
+ return this.settings.animationSpeedMs;
147
151
  }
148
152
 
149
153
  /**
@@ -160,7 +164,7 @@ export default class Cube {
160
164
  this.group.children.forEach((piece) => {
161
165
  const { x, y, z } = piece.userData.initialPosition;
162
166
  const { x: u, y: v, z: w } = piece.userData.initialRotation;
163
- piece.position.set(x * this.settings.gap, y * this.settings.gap, z * this.settings.gap);
167
+ piece.position.set(x * this.settings.pieceGap, y * this.settings.pieceGap, z * this.settings.pieceGap);
164
168
  piece.rotation.set(u, v, w);
165
169
  piece.userData.position.x = x;
166
170
  piece.userData.position.y = y;