@everymatrix/lottery-program-wof 1.3.2
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 +30 -0
- package/dist/lottery-program-wof.js +7278 -0
- package/dist/lottery-program-wof.js.map +1 -0
- package/index.html +42 -0
- package/index.js +1 -0
- package/package.json +41 -0
- package/public/favicon.png +0 -0
- package/public/reset.css +48 -0
- package/rollup.config.js +61 -0
- package/src/LotteryProgramWof.svelte +129 -0
- package/src/api/api.ts +2011 -0
- package/src/api/configuration.ts +65 -0
- package/src/api/custom.d.ts +2 -0
- package/src/api/index.ts +15 -0
- package/src/business.dom.ts +130 -0
- package/src/business.fake.ts +17 -0
- package/src/business.ts +276 -0
- package/src/calc.image.ts +13 -0
- package/src/calc.point.ts +315 -0
- package/src/calc.temp.ts +29 -0
- package/src/calc.ts +34 -0
- package/src/class.spinable.ts +65 -0
- package/src/class.spinable.util.ts +10 -0
- package/src/class.spinner.ts +145 -0
- package/src/class.spinner.util.ts +92 -0
- package/src/css.state.ts +13 -0
- package/src/fakeDraw.ts +9 -0
- package/src/fakeResult.ts +49 -0
- package/src/images/area.svg +11 -0
- package/src/images/areaSec.svg +11 -0
- package/src/images/areaV1.svg +18 -0
- package/src/images/areaV2.svg +17 -0
- package/src/images/background.svg +27 -0
- package/src/images/background3.svg +13 -0
- package/src/images/backgroundShadow.svg +22 -0
- package/src/images/centerArrow.svg +50 -0
- package/src/images/centerArrow1.svg +18 -0
- package/src/images/centerArrow2.svg +5 -0
- package/src/images/centerArrow3.svg +46 -0
- package/src/images/centerArrowBg.svg +12 -0
- package/src/images/centerBackground2.svg +24 -0
- package/src/images/centerCircle.svg +24 -0
- package/src/images/centerPack.svg +16 -0
- package/src/images/centerText3.svg +3 -0
- package/src/images/gift.svg +964 -0
- package/src/images/light.svg +19 -0
- package/src/images/partition1.svg +10 -0
- package/src/images/pointerArrow.svg +24 -0
- package/src/images/pointerArrow3.svg +25 -0
- package/src/images/spin.svg +13 -0
- package/src/index.ts +4 -0
- package/src/message.ts +28 -0
- package/src/private.item.svelte +279 -0
- package/src/private.item.svg.svelte +791 -0
- package/src/private.message.svelte +167 -0
- package/src/private.outcomes.svelte +163 -0
- package/src/private.tabs.svelte +92 -0
- package/src/themes.partitions.ts +174 -0
- package/src/themes.ts +206 -0
- package/src/types.business.ts +4 -0
- package/src/types.ts +74 -0
- package/src/util.ts +83 -0
- package/stories/LotteryProgramWof.stories.js +13 -0
- package/svelte.config.js +7 -0
- package/tsconfig.json +6 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { ArrowMode, Point, PointerMode } from "./types"
|
|
2
|
+
|
|
3
|
+
import { ratioGoldenMean } from "./calc"
|
|
4
|
+
import { mapObjectValueToTuple } from "./util"
|
|
5
|
+
|
|
6
|
+
export const getSymmetricalPointFromScalar =
|
|
7
|
+
(length: number): Point => ({
|
|
8
|
+
x: length,
|
|
9
|
+
y: length
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export const getPointOnCircle =
|
|
13
|
+
(radius: number, angle: number, center: number): Point => {
|
|
14
|
+
|
|
15
|
+
const centerPoint: Point = getSymmetricalPointFromScalar(center)
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
x: centerPoint.x + radius * Math.cos(angle),
|
|
19
|
+
y: centerPoint.y + radius * Math.sin(angle)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const getPoint =
|
|
24
|
+
(num: number, length: number, r: number, rCenter: number): Point => {
|
|
25
|
+
|
|
26
|
+
const arc = 360 / length;
|
|
27
|
+
const psai = Math.PI / 180 * (-90 + arc * (num - 1/2))
|
|
28
|
+
|
|
29
|
+
const rCenterPoint = getSymmetricalPointFromScalar(rCenter)
|
|
30
|
+
const rPoint = getSymmetricalPointFromScalar(r)
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
x: rCenterPoint.x + rPoint.x * Math.cos(psai),
|
|
34
|
+
y: rCenterPoint.y + rPoint.y * Math.sin(psai)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const getPointWithNext =
|
|
39
|
+
(num: number, length: number, r1: number, r2: number) => {
|
|
40
|
+
|
|
41
|
+
const getPointx = (num: number) => getPoint(num, length, r1, r2)
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
point: getPointx(num),
|
|
45
|
+
pointNext: getPointx(num + 1)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const getTrianglePointsY =
|
|
50
|
+
(height: number, isPointUp: boolean, yBase: number) => {
|
|
51
|
+
return {
|
|
52
|
+
y1: yBase,
|
|
53
|
+
y2: isPointUp ? yBase - height : yBase + height,
|
|
54
|
+
y3: yBase
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const getTwoPointsOfLineGradientForBgRing = (radius, angle, center) => {
|
|
59
|
+
const p1 = getPointOnCircle(radius, angle - Math.PI / 2, center)
|
|
60
|
+
const p2 = getPointOnCircle(radius, angle + Math.PI / 2, center)
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
x1: p1.x,
|
|
64
|
+
y1: p1.y,
|
|
65
|
+
x2: p2.x,
|
|
66
|
+
y2: p2.y,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const getTwoPointsOfLineGradient = (radius, index, length, angle) => ({
|
|
71
|
+
x1: radius,
|
|
72
|
+
y1: radius,
|
|
73
|
+
x2: radius * (1 + Math.cos(2 * Math.PI / length * index + angle)),
|
|
74
|
+
y2: radius * (1 + Math.sin(2 * Math.PI / length * index + angle)),
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
export const getTrianglePoints =
|
|
78
|
+
|
|
79
|
+
(center: number, width: number, height: number, arrowMode: ArrowMode, rCircleCenter: number) => {
|
|
80
|
+
|
|
81
|
+
const pointCenter = getSymmetricalPointFromScalar(center)
|
|
82
|
+
|
|
83
|
+
const getYAxisMatrix = () => {
|
|
84
|
+
|
|
85
|
+
switch(arrowMode){
|
|
86
|
+
case ArrowMode.DownFromTop: return getTrianglePointsY(height, false, 0)
|
|
87
|
+
case ArrowMode.DownFromCenter: return getTrianglePointsY(height, false, pointCenter.y + rCircleCenter)
|
|
88
|
+
case ArrowMode.UpFromCenter: return getTrianglePointsY(height, true, pointCenter.y - rCircleCenter)
|
|
89
|
+
case ArrowMode.UpFromBottom: return getTrianglePointsY(height, true, pointCenter.y * 2)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const {y1, y2, y3} = getYAxisMatrix()
|
|
94
|
+
|
|
95
|
+
const points: Point[] = [
|
|
96
|
+
{
|
|
97
|
+
x: pointCenter.x - width/2,
|
|
98
|
+
y: y1
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
x: pointCenter.x,
|
|
102
|
+
y: y2,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
x: pointCenter.x + width/2,
|
|
106
|
+
y: y3
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
const pointsValue =
|
|
111
|
+
points.map(point =>
|
|
112
|
+
Object.keys(point).map(key => point[key]).join(' ')
|
|
113
|
+
).join(', ')
|
|
114
|
+
|
|
115
|
+
return pointsValue
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const getPartitionProps =
|
|
119
|
+
(
|
|
120
|
+
index: number,
|
|
121
|
+
number: number,
|
|
122
|
+
rRingInner: number,
|
|
123
|
+
r: number,
|
|
124
|
+
) => {
|
|
125
|
+
|
|
126
|
+
const rPoint = getSymmetricalPointFromScalar(r)
|
|
127
|
+
const rRingInnerPoint = getSymmetricalPointFromScalar(rRingInner)
|
|
128
|
+
|
|
129
|
+
const { point, pointNext } = getPointWithNext(index, number, rRingInner, r)
|
|
130
|
+
|
|
131
|
+
const ds = [
|
|
132
|
+
`M`,
|
|
133
|
+
...mapObjectValueToTuple(rPoint),
|
|
134
|
+
`L`,
|
|
135
|
+
...mapObjectValueToTuple(point),
|
|
136
|
+
`A`,
|
|
137
|
+
...mapObjectValueToTuple(rRingInnerPoint),
|
|
138
|
+
`0 0 1`,
|
|
139
|
+
...mapObjectValueToTuple(pointNext),
|
|
140
|
+
`Z`,
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
d: ds.join(' ')
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export const getRingImageProps = (index, length, rRingOuter, rRingInner, center, ratio) => {
|
|
149
|
+
|
|
150
|
+
const aver = (rRingOuter + rRingInner) / 2
|
|
151
|
+
const diff = (rRingOuter - rRingInner) / 2 + 4 * ratio
|
|
152
|
+
|
|
153
|
+
const { point } = getPointWithNext(index, length, aver, center)
|
|
154
|
+
|
|
155
|
+
const radius = diff * 2
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
x: point.x - diff,
|
|
159
|
+
y: point.y - diff,
|
|
160
|
+
width: radius,
|
|
161
|
+
height: radius,
|
|
162
|
+
// style: `stroke-width: ${radius * ratioGoldenMean(6)}px`,
|
|
163
|
+
class: "RingImage",
|
|
164
|
+
part: "RingImage",
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const getImagePartitionProps = (index, length, radius) => {
|
|
169
|
+
|
|
170
|
+
const multiple = 1.5
|
|
171
|
+
const size = {
|
|
172
|
+
width: multiple * 2 * radius * Math.sin(2 * Math.PI / length /2),
|
|
173
|
+
height: radius,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const translate = [
|
|
177
|
+
size.width / 2,
|
|
178
|
+
size.height
|
|
179
|
+
// * (1 - 168/197)
|
|
180
|
+
].map(s => `${s * -1}px`).join(',')
|
|
181
|
+
|
|
182
|
+
const rotate = 360 / length * index
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
x: radius - size.width / 2,
|
|
186
|
+
y: radius - size.height,
|
|
187
|
+
...size,
|
|
188
|
+
style: [
|
|
189
|
+
`transform-origin: ${radius}px ${radius}px`,
|
|
190
|
+
`transform: ${[
|
|
191
|
+
`rotate(${rotate}deg)`,
|
|
192
|
+
// `translate(${translate})`,
|
|
193
|
+
].join(' ')}`,
|
|
194
|
+
|
|
195
|
+
].join(';'),
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const getImageProps = (length, radius, center) => {
|
|
200
|
+
|
|
201
|
+
const multiple = 1
|
|
202
|
+
|
|
203
|
+
const size = {
|
|
204
|
+
width: multiple * 2 * radius * Math.sin(2 * Math.PI / length /2),
|
|
205
|
+
height: multiple * radius,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
x: center,
|
|
210
|
+
y: center,
|
|
211
|
+
...size,
|
|
212
|
+
style: `transform: translate(${[
|
|
213
|
+
size.width / 2,
|
|
214
|
+
size.height
|
|
215
|
+
// * (1 - 168/197)
|
|
216
|
+
].map(s => `${s * -1}px`).join(',')})`,
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export const getRingCircleProps = (index, length, rRingOuter, rRingInner, r) => {
|
|
221
|
+
|
|
222
|
+
const aver = (rRingOuter + rRingInner) / 2
|
|
223
|
+
const diff = (rRingOuter - rRingInner) / 2
|
|
224
|
+
|
|
225
|
+
const { point } = getPointWithNext(index, length, aver, r)
|
|
226
|
+
|
|
227
|
+
const radius = diff * 0.618
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
cx: point.x,
|
|
231
|
+
cy: point.y,
|
|
232
|
+
r: radius,
|
|
233
|
+
style: `stroke-width: ${radius * ratioGoldenMean(6)}px`,
|
|
234
|
+
class: "RingCircle",
|
|
235
|
+
part: "RingCircle",
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const angleTransform = (pointermode, arrowmode) => {
|
|
240
|
+
switch(pointermode){
|
|
241
|
+
case PointerMode.Arrow:
|
|
242
|
+
switch(arrowmode){
|
|
243
|
+
case ArrowMode.DownFromTop:
|
|
244
|
+
case ArrowMode.UpFromCenter:
|
|
245
|
+
return - Math.PI / 2
|
|
246
|
+
|
|
247
|
+
case ArrowMode.DownFromCenter:
|
|
248
|
+
case ArrowMode.UpFromBottom:
|
|
249
|
+
return Math.PI / 2
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
case PointerMode.Partition:
|
|
253
|
+
return - Math.PI / 2
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
enum RotateDirection {
|
|
258
|
+
clockwise = 1,
|
|
259
|
+
anticlockwise = -1,
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const pointMinus = (point1: Point, point2: Point): Point => ({
|
|
263
|
+
x: point1.x - point2.x,
|
|
264
|
+
y: point1.y - point2.y
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
const pointPlus = (point1: Point, point2: Point): Point => ({
|
|
268
|
+
x: point1.x + point2.x,
|
|
269
|
+
y: point1.y + point2.y
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
const getPropsCommonTransform = (ratio: number, index: number, length: number, point: Point, offset: Point, direction: RotateDirection) => {
|
|
273
|
+
|
|
274
|
+
const angleSelf = 360 * index / length * direction -90
|
|
275
|
+
|
|
276
|
+
const transformOrigin: Point = pointPlus(point, offset)
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
style: [
|
|
280
|
+
`font-size: ${13 * ratio}px`,
|
|
281
|
+
`transform: rotate(${angleSelf}deg)`,
|
|
282
|
+
`transform-origin: ${transformOrigin.x}px ${transformOrigin.y}px`,
|
|
283
|
+
].join(';')
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export const getPropsForPartitionInfo = (
|
|
288
|
+
ratio: number,
|
|
289
|
+
radius: number,
|
|
290
|
+
center: number,
|
|
291
|
+
index: number,
|
|
292
|
+
length: number,
|
|
293
|
+
pointermode: PointerMode,
|
|
294
|
+
arrowmode: ArrowMode,
|
|
295
|
+
offset: any
|
|
296
|
+
) => {
|
|
297
|
+
|
|
298
|
+
const direction = RotateDirection.clockwise
|
|
299
|
+
|
|
300
|
+
const angle =
|
|
301
|
+
angleTransform(pointermode, arrowmode) +
|
|
302
|
+
direction * index * 2 * Math.PI / length
|
|
303
|
+
|
|
304
|
+
const point = getPointOnCircle(radius - 9 * ratio, angle, center + offset.center)
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
...pointMinus(point, offset.position),
|
|
308
|
+
...(pointermode === PointerMode.Arrow || true ?
|
|
309
|
+
getPropsCommonTransform(ratio, index, length, point, offset.transform, direction)
|
|
310
|
+
:
|
|
311
|
+
{}
|
|
312
|
+
)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
package/src/calc.temp.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const byte2Hex = (n: number) => {
|
|
2
|
+
var nybHexString = "0123456789ABCDEF";
|
|
3
|
+
const query = (m: number) => nybHexString.substr(m & 0x0F, 1)
|
|
4
|
+
|
|
5
|
+
return String(query(n >> 4)) + query(n)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function getColor(item: number, maxitem: number) {
|
|
9
|
+
|
|
10
|
+
const colorCalc = (x: number) => {
|
|
11
|
+
|
|
12
|
+
const phase = 0;
|
|
13
|
+
const center = 128;
|
|
14
|
+
const width = 127;
|
|
15
|
+
|
|
16
|
+
const frequency = 2 * Math.PI / maxitem;
|
|
17
|
+
|
|
18
|
+
return Math.sin(frequency * item + x + phase) * width + center
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const hex = (x: number) => byte2Hex(colorCalc(x))
|
|
22
|
+
|
|
23
|
+
return [
|
|
24
|
+
'#',
|
|
25
|
+
hex(2),
|
|
26
|
+
hex(0),
|
|
27
|
+
hex(4),
|
|
28
|
+
].join('')
|
|
29
|
+
}
|
package/src/calc.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const convertDegToArc = (deg: number) => deg * Math.PI / 180
|
|
2
|
+
export const convertArcToDeg = (arc: number) => arc * 180 / Math.PI % 360
|
|
3
|
+
|
|
4
|
+
export const getArcDelta = (n: number) => 2 * Math.PI / n
|
|
5
|
+
|
|
6
|
+
export const findIndex = (deg: number, n: number) =>
|
|
7
|
+
Math.floor(
|
|
8
|
+
(360 - deg % 360) / convertArcToDeg(getArcDelta(n))
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
export const findDeg = (index: number, n: number) => {
|
|
12
|
+
const degDelta = convertArcToDeg(getArcDelta(n))
|
|
13
|
+
return (index) * degDelta
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const createSVGElement = (tag: string) => {
|
|
17
|
+
const xmlns = "http://www.w3.org/2000/svg";
|
|
18
|
+
return document.createElementNS(xmlns, tag);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const ratioGoldenMean = (times: number) => Math.pow(0.618, times)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
const randomDirection = () => Math.random() > 0.5 ? 1 : -1
|
|
25
|
+
|
|
26
|
+
export const randomInSection = (number: number) => {
|
|
27
|
+
return 360 / number / 2 * (1 - 0.2) * Math.random() * randomDirection()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const getArcLength = (radius: number, degrees: number) => {
|
|
31
|
+
const circumference = 2 * Math.PI * radius;
|
|
32
|
+
const arcLength = (degrees / 360) * circumference;
|
|
33
|
+
return arcLength;
|
|
34
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { convertDegToArc, easeOut } from "./class.spinable.util"
|
|
2
|
+
|
|
3
|
+
export abstract class Spinnable {
|
|
4
|
+
arcStart: number = 0
|
|
5
|
+
|
|
6
|
+
spinTime = 0
|
|
7
|
+
spinTimeTotal = Math.random() * 3 + 8 * 1000
|
|
8
|
+
spinAngleStart = 0
|
|
9
|
+
spinTimeout
|
|
10
|
+
public spinable: boolean = true
|
|
11
|
+
|
|
12
|
+
abstract tick(): void;
|
|
13
|
+
abstract beforeStart(deg: number): void;
|
|
14
|
+
abstract afterStop(): void;
|
|
15
|
+
|
|
16
|
+
public spin(deg, fn?){
|
|
17
|
+
this.spinAngleStart = deg
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if(!this.spinable) return;
|
|
21
|
+
|
|
22
|
+
this.spinable = false
|
|
23
|
+
console.log('x', this.arcStart);
|
|
24
|
+
console.log('x', this.spinAngleStart);
|
|
25
|
+
this.spinTimeTotal = 1000
|
|
26
|
+
this.spinTime = 0;
|
|
27
|
+
this.spinTick(fn)
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private spinTick(fn) {
|
|
32
|
+
this.spinTime += 30
|
|
33
|
+
|
|
34
|
+
if(this.spinTime >= this.spinTimeTotal) {
|
|
35
|
+
this.stop(fn);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
var spinAngle =
|
|
40
|
+
this.spinAngleStart -
|
|
41
|
+
easeOut(
|
|
42
|
+
this.spinTime,
|
|
43
|
+
0,
|
|
44
|
+
this.spinAngleStart,
|
|
45
|
+
this.spinTimeTotal
|
|
46
|
+
);
|
|
47
|
+
console.log('ease', spinAngle);
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
this.arcStart += convertDegToArc(spinAngle);
|
|
51
|
+
console.log('arcStart', this.arcStart);
|
|
52
|
+
|
|
53
|
+
this.tick()
|
|
54
|
+
this.spinTimeout = setTimeout(this.spinTick.bind(this, fn), 10);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private stop (fn){
|
|
58
|
+
clearTimeout(this.spinTimeout);
|
|
59
|
+
console.log(this.arcStart);
|
|
60
|
+
|
|
61
|
+
this.afterStop()
|
|
62
|
+
this.spinable = true
|
|
63
|
+
if(fn) fn()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
export const convertDegToArc = (deg: number) => deg * Math.PI / 180
|
|
3
|
+
export const easeOut =
|
|
4
|
+
(time: number, b: number, angleStart: number, timeTotal: number) => {
|
|
5
|
+
var ts = (time /= timeTotal) * time;
|
|
6
|
+
var tc = ts * time;
|
|
7
|
+
const res = b + angleStart * (tc + -3 * ts + 3 * time);
|
|
8
|
+
|
|
9
|
+
return res
|
|
10
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { easing, easingGrad, params, SpinStep, Easing, easingGradX } from "./class.spinner.util";
|
|
2
|
+
|
|
3
|
+
export class Spinner {
|
|
4
|
+
|
|
5
|
+
deg: number = 0
|
|
6
|
+
// spinable: boolean = true
|
|
7
|
+
degTarget: number = undefined
|
|
8
|
+
container: HTMLElement = undefined
|
|
9
|
+
step: SpinStep = undefined
|
|
10
|
+
tick
|
|
11
|
+
cb
|
|
12
|
+
easingType: Easing = Easing.cubic
|
|
13
|
+
shouldHalt
|
|
14
|
+
|
|
15
|
+
param = params[2]
|
|
16
|
+
|
|
17
|
+
public launch(){
|
|
18
|
+
this.shouldHalt = undefined
|
|
19
|
+
this.step = SpinStep.launch
|
|
20
|
+
this.ticker()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public halt = (deg, cb = () => {}) => {
|
|
24
|
+
this.shouldHalt = () => {
|
|
25
|
+
this.step = SpinStep.halt
|
|
26
|
+
this.cb = () => {
|
|
27
|
+
cb()
|
|
28
|
+
this.setDeg(deg, 0)
|
|
29
|
+
}
|
|
30
|
+
this.degTarget = deg + 360 * this.param.haltTurns
|
|
31
|
+
this.continueRepeat()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private continueRepeat = () => requestAnimationFrame(this.ticker)
|
|
36
|
+
private ticker = () => {
|
|
37
|
+
|
|
38
|
+
const linkSpeed = this.param.loopSpeed
|
|
39
|
+
|
|
40
|
+
const easingDuration = (deg: number) =>
|
|
41
|
+
30 * easingGrad[this.easingType] * deg / this.param.loopSpeed
|
|
42
|
+
|
|
43
|
+
const animations = {
|
|
44
|
+
launch: () => {
|
|
45
|
+
const t = easingDuration(360 * this.param.launchTurns - this.deg)
|
|
46
|
+
this.animate(t, this.deg, 360 * this.param.launchTurns - this.deg, easing.in[this.easingType], easingGradX.in[this.easingType])
|
|
47
|
+
},
|
|
48
|
+
halt: () => {
|
|
49
|
+
const t = easingDuration(this.degTarget)
|
|
50
|
+
this.animate(t, 0, this.degTarget, easing.out[this.easingType], easingGradX.out[this.easingType])
|
|
51
|
+
},
|
|
52
|
+
loop: () => {
|
|
53
|
+
this.setDeg((this.deg + linkSpeed) % 360, easingGrad[this.easingType])
|
|
54
|
+
this.continueRepeat()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const stepSwitcher = {
|
|
59
|
+
[SpinStep.launch]: () => {
|
|
60
|
+
this.cb = () => {
|
|
61
|
+
this.step = SpinStep.loop
|
|
62
|
+
this.setDeg(linkSpeed, easingGrad[this.easingType])
|
|
63
|
+
this.continueRepeat()
|
|
64
|
+
}
|
|
65
|
+
animations.launch()
|
|
66
|
+
},
|
|
67
|
+
[SpinStep.loop]: () => {
|
|
68
|
+
if(this.shouldHalt && this.deg === 0){
|
|
69
|
+
this.shouldHalt()
|
|
70
|
+
}else{
|
|
71
|
+
animations.loop()
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
[SpinStep.halt]: () => {
|
|
75
|
+
if(this.deg === 360 - linkSpeed){
|
|
76
|
+
animations.halt()
|
|
77
|
+
}else{
|
|
78
|
+
animations.loop()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
stepSwitcher[this.step]()
|
|
84
|
+
|
|
85
|
+
// switch(this.step){
|
|
86
|
+
// case SpinStep.launch:
|
|
87
|
+
|
|
88
|
+
// break
|
|
89
|
+
// case SpinStep.loop:
|
|
90
|
+
|
|
91
|
+
// if(this.shouldHalt && this.deg === 0){
|
|
92
|
+
// this.shouldHalt()
|
|
93
|
+
// }else{
|
|
94
|
+
|
|
95
|
+
// this.setDeg((this.deg + linkSpeed) % 360, easingGrad[this.easingType])
|
|
96
|
+
// this.continueRepeat()
|
|
97
|
+
// }
|
|
98
|
+
// break
|
|
99
|
+
// case SpinStep.halt:
|
|
100
|
+
|
|
101
|
+
// if(this.deg === 360 - linkSpeed){
|
|
102
|
+
|
|
103
|
+
// const t = 30 * easingGrad[this.easingType] * this.degTarget / this.param.loopSpeed
|
|
104
|
+
// this.animate(t, 0, this.degTarget, easing.out[this.easingType], easingGradX.out[this.easingType])
|
|
105
|
+
// }else{
|
|
106
|
+
// this.setDeg((this.deg + linkSpeed) % 360, easingGrad[this.easingType])
|
|
107
|
+
// this.continueRepeat()
|
|
108
|
+
// }
|
|
109
|
+
// break
|
|
110
|
+
// }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private setDeg = (deg, speed) => {
|
|
114
|
+
this.deg = deg
|
|
115
|
+
this.tick(deg, speed)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
animate = (duration: number, startAngle, target: number, easing, _easingGrad) => {
|
|
119
|
+
|
|
120
|
+
let animationId = null;
|
|
121
|
+
window.cancelAnimationFrame(animationId);
|
|
122
|
+
let start = null;
|
|
123
|
+
|
|
124
|
+
const step = (timestamp) => {
|
|
125
|
+
|
|
126
|
+
if (!start) {
|
|
127
|
+
start = timestamp;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const progress = timestamp - start;
|
|
131
|
+
const translation = target * easing(progress / duration) + startAngle;
|
|
132
|
+
const speedCur = Math.abs(_easingGrad(progress / duration))
|
|
133
|
+
|
|
134
|
+
this.setDeg(translation, speedCur)
|
|
135
|
+
|
|
136
|
+
if (progress < duration) {
|
|
137
|
+
animationId = window.requestAnimationFrame(step);
|
|
138
|
+
}else{
|
|
139
|
+
this.cb()
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
animationId = window.requestAnimationFrame(step);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
|
|
2
|
+
export enum SpinStep {
|
|
3
|
+
launch = 'launch',
|
|
4
|
+
loop = 'loop',
|
|
5
|
+
halt = 'halt',
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export enum Easing {
|
|
9
|
+
quad = 'quad',
|
|
10
|
+
cubic = 'cubic',
|
|
11
|
+
quart = 'quart',
|
|
12
|
+
quint = 'quint',
|
|
13
|
+
//
|
|
14
|
+
expo = 'expo',
|
|
15
|
+
sine = 'sine',
|
|
16
|
+
circ = 'circ',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const easing = {
|
|
20
|
+
in: {
|
|
21
|
+
quad: (x: number) => Math.pow(x, 2),
|
|
22
|
+
cubic: (x: number) => Math.pow(x, 3),
|
|
23
|
+
quart: (x: number) => Math.pow(x, 4),
|
|
24
|
+
quint: (x: number) => Math.pow(x, 5),
|
|
25
|
+
//
|
|
26
|
+
sine: (x: number) => 1 - Math.cos((x * Math.PI) / 2),
|
|
27
|
+
expo: (x: number) => x === 0 ? 0 : Math.pow(2, 10 * x - 10),
|
|
28
|
+
circ: (x: number) => 1 - Math.sqrt(1 - Math.pow(x, 2)),
|
|
29
|
+
},
|
|
30
|
+
out: {
|
|
31
|
+
quad: (x: number) => 1 - Math.pow(1 - x, 2),
|
|
32
|
+
cubic: (x: number) => 1 - Math.pow(1 - x, 3),
|
|
33
|
+
quart: (x: number) => 1 - Math.pow(1 - x, 4),
|
|
34
|
+
quint: (x: number) => 1 - Math.pow(1 - x, 5),
|
|
35
|
+
//
|
|
36
|
+
sine: (x: number) => Math.sin((x * Math.PI) / 2),
|
|
37
|
+
expo: (x: number) => x === 1 ? 1 : 1 - Math.pow(2, -10 * x),
|
|
38
|
+
circ: (x: number) => Math.sqrt(1 - Math.pow(x - 1, 2)),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const easingGrad = {
|
|
43
|
+
quad: 2,
|
|
44
|
+
cubic: 3,
|
|
45
|
+
quart: 4,
|
|
46
|
+
quint: 5,
|
|
47
|
+
sine: Math.PI / 2,
|
|
48
|
+
expo: Math.LN2,
|
|
49
|
+
circ: Infinity,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const easingGradX = {
|
|
53
|
+
in: {
|
|
54
|
+
quad: (x: number) => 2 * x,
|
|
55
|
+
cubic: (x: number) => 3 * x ** 2,
|
|
56
|
+
quart: (x: number) => 4 * x ** 3,
|
|
57
|
+
quint: (x: number) => 5 * x ** 4,
|
|
58
|
+
sine: (x: number) => Math.PI / 2 * Math.sin(Math.PI / 2 * x),
|
|
59
|
+
expo: (x: number) => Math.LN2 * 10 * 2 ** (10 * x - 10),
|
|
60
|
+
circ: (x: number) => -1 / 2 * (1 - x ** 2) ** (- 1 / 2),
|
|
61
|
+
},
|
|
62
|
+
out: {
|
|
63
|
+
cubic: (x: number) => 3 * x ** 2 - 6 * x + 3,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const params = [
|
|
68
|
+
// basic
|
|
69
|
+
{
|
|
70
|
+
launchTurns: 5,
|
|
71
|
+
launchSpeed: 0.2,
|
|
72
|
+
loopSpeed: 15,
|
|
73
|
+
haltTurns: 2,
|
|
74
|
+
haltSpeed: 0.16,
|
|
75
|
+
},
|
|
76
|
+
// faster
|
|
77
|
+
{
|
|
78
|
+
launchTurns: 2,
|
|
79
|
+
launchSpeed: 0.5,
|
|
80
|
+
loopSpeed: 20,
|
|
81
|
+
haltTurns: 2,
|
|
82
|
+
haltSpeed: 0.22,
|
|
83
|
+
},
|
|
84
|
+
// faster with more turns
|
|
85
|
+
{
|
|
86
|
+
launchTurns: 6,
|
|
87
|
+
launchSpeed: 1.38,
|
|
88
|
+
loopSpeed: 60,
|
|
89
|
+
haltTurns: 6,
|
|
90
|
+
haltSpeed: 0.50,
|
|
91
|
+
},
|
|
92
|
+
]
|
package/src/css.state.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
export const getStatePropsForSpin = (deg: number, rotation: number, duration: number) => {
|
|
3
|
+
return {
|
|
4
|
+
before: {
|
|
5
|
+
style: `transition-timing-function: ease-in-out; transition-duration: ${duration}s;`,
|
|
6
|
+
transform: `rotate(${deg + 360 * rotation})`,
|
|
7
|
+
},
|
|
8
|
+
after: {
|
|
9
|
+
style: '',
|
|
10
|
+
transform: `rotate(${deg})`,
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|