@operato/scene-gauge 8.0.0-beta.1 → 9.0.0-beta.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,321 +0,0 @@
1
- /*
2
- * Copyright © HatioLab Inc. All rights reserved.
3
- */
4
-
5
- import { Component, ComponentNature, Donut, ValueHolder } from '@hatiolab/things-scene'
6
-
7
- import { PROPERTIES } from './gauge-properties'
8
-
9
- const NATURE: ComponentNature = {
10
- mutable: false,
11
- resizable: true,
12
- rotatable: true,
13
- properties: [
14
- ...PROPERTIES,
15
- {
16
- type: 'number',
17
- label: 'start-angle',
18
- name: 'startAngle',
19
- property: 'startAngle'
20
- },
21
- {
22
- type: 'number',
23
- label: 'end-angle',
24
- name: 'endAngle',
25
- property: 'endAngle'
26
- },
27
- {
28
- type: 'color',
29
- label: 'text-fill-style',
30
- name: 'textFillStyle',
31
- property: 'textFillStyle'
32
- },
33
- {
34
- type: 'color',
35
- label: 'needle-fill-style',
36
- name: 'needleFillStyle',
37
- property: 'needleFillStyle'
38
- },
39
- {
40
- type: 'color',
41
- label: 'inner-circle-fill-style',
42
- name: 'innerCircleFillStyle',
43
- property: 'innerCircleFillStyle'
44
- },
45
- {
46
- type: 'checkbox',
47
- label: 'in-text',
48
- name: 'inText',
49
- property: 'inText'
50
- }
51
- ],
52
- help: 'scene/component/gauge-circle'
53
- }
54
-
55
- function countSignificantFigures(number: number) {
56
- const numStr = number.toString()
57
- const parts = numStr.split('.')
58
-
59
- // 소수점 이하 부분을 분석
60
- if (parts.length === 2) {
61
- const decimalPart = parts[1]
62
- let significantFigures = 0
63
-
64
- for (let i = 0; i < decimalPart.length; i++) {
65
- if (decimalPart[i] !== '0') {
66
- significantFigures = decimalPart.length - i
67
- break
68
- }
69
- }
70
-
71
- return significantFigures
72
- }
73
-
74
- // 정수인 경우 유효 자릿수 없음
75
- return 0
76
- }
77
-
78
- function drawStepLine(context: CanvasRenderingContext2D, ang: number, rx: number, stepNeedleSize: number) {
79
- context.rotate(ang)
80
- context.translate(0, -rx)
81
-
82
- context.fillRect(0, -rx * 0.14, stepNeedleSize, rx * 0.175)
83
- context.translate(0, rx)
84
- context.rotate(-ang)
85
- }
86
-
87
- function drawSubStepLine(context: CanvasRenderingContext2D, ang: number, rx: number, stepNeedleSize: number) {
88
- context.rotate(ang)
89
- context.translate(0, -rx)
90
-
91
- context.fillRect(0, -rx * 0.04, stepNeedleSize, rx * 0.075)
92
- context.translate(0, rx)
93
- context.rotate(-ang)
94
- }
95
-
96
- function drawStepText(context: CanvasRenderingContext2D, text: string, ang: number, rx: number) {
97
- context.rotate(ang)
98
- context.translate(0, -rx * 0.83)
99
- context.rotate(-ang)
100
-
101
- context.fillText(text, 0, 0)
102
- context.rotate(ang)
103
- context.translate(0, rx * 0.83)
104
- context.rotate(-ang)
105
- }
106
-
107
- function drawNeedle(context: CanvasRenderingContext2D, rx: number, ang: number) {
108
- context.rotate(ang)
109
-
110
- context.beginPath()
111
-
112
- context.moveTo(rx * 0.035, 0) // 중앙 두께
113
-
114
- context.lineTo(0, rx * 0.8) // 끝 점
115
-
116
- context.lineTo(-rx * 0.035, 0) // 중앙 두께
117
-
118
- context.lineTo(-rx * 0.015, -rx * 0.2) // 뒷쪽 두께
119
- context.lineTo(rx * 0.015, -rx * 0.2) // 뒷쪽 두께
120
-
121
- context.rotate(-ang)
122
- }
123
-
124
- export default class GaugeCircle extends ValueHolder(Donut) {
125
- render(context: CanvasRenderingContext2D) {
126
- var {
127
- lineWidth = 20,
128
- startValue,
129
- endValue,
130
- step,
131
- subStep,
132
- startAngle = 0,
133
- endAngle = 180,
134
- fontColor = 'black',
135
- showStepText = true,
136
- showStartValue = true,
137
- showEndValue = true,
138
- showStepLine = true,
139
- showSubStep = true,
140
- inText = true,
141
- colorStops, // 스텝별 각각 다른 색
142
- fillStyle,
143
- textFillStyle = 'black',
144
- needleFillStyle = 'black',
145
- innerCircleFillStyle = 'gray',
146
- stepNeedleSize = 2,
147
- stepFillStyle,
148
- stepTextSize,
149
- cx,
150
- cy,
151
- rx,
152
- ry,
153
- ratio,
154
- animFromBase = false
155
- } = this.state
156
-
157
- startValue = Number(startValue)
158
-
159
- this.animOnValueChange(this.value, animFromBase, startValue)
160
-
161
- const RADIAN = 0.0174533 / Math.PI
162
- const rxRatio = (rx / 100) * ratio // 원 안에 지워지는 비율을 계산한 rx - ratio의 비율에 따라 크기가 변함
163
- const ryRatio = (ry / 100) * ratio // 원 안에 지워지는 비율을 계산한 ry - ratio의 비율에 따라 크기가 변함
164
- const circleSize = (endAngle - startAngle) / 180 // 원의 총 길이. - PI * 2가 원이므로 (360도 = 2, 180도 = 1)
165
- const totalValue = endValue - startValue // 게이지의 시작과 끝 값의 크기
166
-
167
- startAngle = startAngle * RADIAN - 0.5 // 맨 위쪽을 중심으로 앵글의 범위에 따라 왼쪽으로 넓어짐
168
- endAngle = endAngle * RADIAN - 0.5 // 맨 위쪽을 중심으로 앵글의 범위에 따라 오른쪽으로 넓어짐
169
-
170
- context.translate(cx, cy)
171
-
172
- //// 메인 게이지 원 그리기 ////
173
- context.beginPath()
174
-
175
- context.ellipse(0, 0, Math.abs(rx), Math.abs(ry), 0, startAngle * Math.PI, endAngle * Math.PI)
176
- this.drawStroke(context)
177
- context.ellipse(0, 0, Math.abs(rxRatio), Math.abs(ryRatio), 0, endAngle * Math.PI, startAngle * Math.PI, true) // 반대로 그리며 원을 지움.
178
- // this.drawFill(context)
179
-
180
- context.closePath()
181
-
182
- //// 스텝별 색 칠하기 ////
183
- if (colorStops) {
184
- let beforeValue = 0
185
- let endStepAngle = 0
186
- context.moveTo(0, 0)
187
-
188
- colorStops.forEach(function (
189
- v: { position: number; color: string },
190
- idx: number,
191
- arr: { position: number; color: string }[]
192
- ) {
193
- context.beginPath()
194
-
195
- let value = Math.max(Math.min(v.position - startValue, totalValue), 0) // v.position 범위의 최소값은 0, 최대값은 totalValue가 되야함.
196
- let startStepAngle = endStepAngle || Math.PI * (startAngle + (circleSize * beforeValue) / totalValue)
197
-
198
- if (idx === arr.length - 1)
199
- // 마지막값은 무조건 끝까지 채워주도록 한다
200
- endStepAngle = Math.PI * (startAngle + circleSize)
201
- else endStepAngle = Math.PI * (startAngle + (circleSize * value) / totalValue)
202
-
203
- if (beforeValue > totalValue || beforeValue > value)
204
- // 값이 게이지의 최대 값을 넘어가거나 이전 값 보다 현재값이 작으면 다시 그릴 필요 없음
205
- return false
206
-
207
- context.ellipse(0, 0, Math.abs(rx), Math.abs(ry), 0, startStepAngle, endStepAngle)
208
- context.ellipse(0, 0, Math.abs(rxRatio), Math.abs(ryRatio), 0, endStepAngle, startStepAngle, true)
209
- context.fillStyle = v.color
210
- context.fill()
211
-
212
- beforeValue = value
213
- })
214
- }
215
- context.scale(1, ry / rx)
216
-
217
- //// 바늘 그리기 ////
218
- context.beginPath()
219
- let drawingValue = this.animValue
220
- drawingValue = Math.max(Math.min(drawingValue, endValue), startValue) // 그려지는 값은 startValue보다 작을 수 없고, endValue보다 클 수 없음.
221
-
222
- let ang = Math.PI * ((circleSize * (drawingValue - startValue)) / totalValue + startAngle - 0.5)
223
-
224
- drawNeedle(context, rx, ang)
225
-
226
- context.fillStyle = needleFillStyle
227
- context.fill()
228
-
229
- //// 중앙 원 그리기 ////
230
- context.beginPath()
231
- context.ellipse(0, 0, Math.abs(rx) / 15, Math.abs(rx) / 15, 0, 0, 2 * Math.PI)
232
- context.fillStyle = innerCircleFillStyle
233
- context.fill()
234
-
235
- //// 스텝 선 그리기 ////
236
- context.fillStyle = stepFillStyle
237
- if (showStepLine) {
238
- let count = totalValue / step
239
-
240
- // Draw StartValue
241
- drawStepLine(context, Math.PI * (startAngle + 0.5), rx * 0.8, stepNeedleSize) // 원을 그릴때 PI는 45도 부터 그리지만 angle은 0도부터 틀어서 + 0.5도를 곱해줘야함
242
- // Draw StepValue
243
- for (let num = 1; num < count; num++) {
244
- let ang = Math.PI * ((circleSize / count) * num + startAngle + 0.5)
245
-
246
- drawStepLine(context, ang, rx * 0.8, stepNeedleSize)
247
- }
248
- // Draw EndValue
249
- drawStepLine(context, Math.PI * (endAngle + 0.5), rx * 0.8, stepNeedleSize)
250
- }
251
-
252
- //// 서브 스탭 그리기 ////
253
- if (showSubStep && subStep > 0) {
254
- let count = Math.round(totalValue / subStep)
255
-
256
- // Draw StepValue
257
- for (let num = 1; num <= count; num++) {
258
- if ((num * subStep) % step == 0) {
259
- // 메인 스탭과 서브 스탭은 그리지 않음
260
- continue
261
- }
262
- let ang = Math.PI * ((circleSize / count) * num + startAngle + 0.5)
263
-
264
- drawSubStepLine(context, ang, rx * 0.8, stepNeedleSize)
265
- }
266
- }
267
-
268
- //// 스텝 텍스트 그리기 ////
269
- context.fillStyle = textFillStyle
270
- context.font = (rx * stepTextSize) / 50 + 'px arial'
271
- context.textBaseline = 'middle'
272
- context.textAlign = 'center'
273
- let textLocation = inText ? 0.8 : 1.35
274
-
275
- if (showStartValue) {
276
- // Draw StartText
277
- drawStepText(context, startValue, Math.PI * (startAngle + 0.5), rx * textLocation)
278
- }
279
-
280
- if (showEndValue) {
281
- // Draw EndText
282
- drawStepText(context, endValue, Math.PI * (endAngle + 0.5), rx * textLocation)
283
- }
284
-
285
- if (showStepText) {
286
- // Draw StepText
287
- let count = totalValue / step
288
- let significantFigures = countSignificantFigures(step)
289
-
290
- for (let num = 1; num < count; num++) {
291
- let value = startValue + step * num
292
- let ang = Math.PI * ((circleSize / count) * num + startAngle + 0.5)
293
-
294
- drawStepText(context, value.toFixed(significantFigures), ang, rx * textLocation)
295
- }
296
- }
297
-
298
- context.scale(1, rx / ry)
299
- context.translate(-cx, -cy)
300
- }
301
-
302
- contains(x: number, y: number): boolean {
303
- // 컨테인즈는 Ellipse로 정의함
304
- var { cx, cy, rx, ry } = this.state
305
-
306
- var normx = (x - cx) / (rx * 2 - 0.5)
307
- var normy = (y - cy) / (ry * 2 - 0.5)
308
-
309
- return normx * normx + normy * normy < 0.25
310
- }
311
-
312
- postrender(context: CanvasRenderingContext2D) {
313
- this.drawText(context)
314
- }
315
-
316
- get nature() {
317
- return NATURE
318
- }
319
- }
320
-
321
- Component.register('gauge-circle', GaugeCircle)
@@ -1,203 +0,0 @@
1
- /*
2
- * Copyright © HatioLab Inc. All rights reserved.
3
- */
4
-
5
- import { Component, ComponentNature, RectPath, Shape, ValueHolder } from '@hatiolab/things-scene'
6
-
7
- import { PROPERTIES } from './gauge-properties'
8
-
9
- const NATURE: ComponentNature = {
10
- mutable: false,
11
- resizable: true,
12
- rotatable: true,
13
- properties: [
14
- ...PROPERTIES,
15
- {
16
- type: 'color',
17
- label: 'text-fill-style',
18
- name: 'textFillStyle',
19
- property: 'textFillStyle'
20
- },
21
- {
22
- type: 'color',
23
- label: 'needle-fill-style',
24
- name: 'needleFillStyle',
25
- property: 'needleFillStyle'
26
- },
27
- {
28
- type: 'number',
29
- label: 'needle-size',
30
- name: 'needleSize',
31
- property: 'needleSize'
32
- }
33
- ],
34
- help: 'scene/component/gauge-horizon'
35
- }
36
-
37
- export default class GaugeHorizon extends ValueHolder(RectPath(Shape)) {
38
- render(context: CanvasRenderingContext2D) {
39
- var {
40
- startValue,
41
- endValue,
42
- step,
43
- subStep,
44
- colorStops,
45
- needleFillStyle,
46
- stepFillStyle,
47
- textFillStyle,
48
- needleSize,
49
- stepNeedleSize,
50
- stepTextSize,
51
- showStepText = true,
52
- showStartValue = true,
53
- showEndValue = true,
54
- showStepLine = true,
55
- showSubStep = true,
56
- width,
57
- height,
58
- top,
59
- left,
60
- animFromBase = false
61
- } = this.state
62
-
63
- startValue = Number(startValue)
64
-
65
- this.animOnValueChange(this.value, animFromBase, startValue)
66
-
67
- const totalValue = endValue - startValue // 게이지의 시작과 끝 값의 크기
68
-
69
- context.translate(left, top) // top과 left의 좌표에 영향을 받지 않기 위해 transalate를 한다음 모든 탑과 레프트의 좌표를 0으로 줌
70
-
71
- //// 메인 막대 그리기 ////
72
- context.beginPath()
73
-
74
- context.rect(0, 0, width, height)
75
-
76
- this.drawFill(context)
77
- this.drawStroke(context)
78
- context.closePath()
79
-
80
- //// 스텝별 색 칠하기 ////
81
- if (colorStops) {
82
- let beforeValue = 0
83
- colorStops.forEach(function (
84
- v: { position: number; color: string },
85
- idx: number,
86
- arr: { position: number; color: string }[]
87
- ) {
88
- context.beginPath()
89
-
90
- let value = Math.max(Math.min(v.position - startValue, totalValue), 0) // v.position 범위의 최소값은 0, 최대값은 totalValue가 되야함.
91
- let startStepPosition = (width * beforeValue) / totalValue
92
- let endStepPosition
93
- // console.log(startStepPosition + (width * value / totalValue));
94
- if (idx == arr.length - 1 || startStepPosition + (width * value) / totalValue)
95
- // 배열의 마지막 값이거나 중간 시작값 + 그려지는 값이 width 를 넘을 경우는 무조건 끝까지 채워주도록 한다
96
- endStepPosition = width - startStepPosition
97
- else endStepPosition = (width * value) / totalValue
98
-
99
- if (beforeValue > totalValue || beforeValue > value)
100
- // 값이 게이지의 최대 값을 넘어가거나 이전 값 보다 현재값이 작으면 다시 그릴 필요 없음
101
- return false
102
-
103
- context.rect(startStepPosition, 0, endStepPosition, height)
104
-
105
- context.fillStyle = v.color
106
- context.fill()
107
-
108
- beforeValue = value
109
- })
110
- }
111
-
112
- //// 스텝 선 그리기 ////
113
- context.fillStyle = stepFillStyle
114
- if (showStepLine) {
115
- let count = totalValue / step
116
- let stepSize = width * 0.06
117
-
118
- // Draw StartValue
119
- context.fillRect(0, height - stepSize, stepNeedleSize, stepSize)
120
- // Draw StepValue
121
- for (let num = 1; num < count; num++) {
122
- let locate = (width / count) * num
123
-
124
- context.fillRect(locate, height - stepSize, stepNeedleSize, stepSize)
125
- }
126
- // Draw EndValue
127
- context.fillRect(width, height - stepSize, stepNeedleSize, stepSize)
128
- }
129
-
130
- //// 서브 스탭 그리기 ////
131
- if (showSubStep) {
132
- let count = totalValue
133
- let subStepSize = width * 0.027
134
- // Draw StartValue
135
- context.fillRect(0, height - subStepSize, stepNeedleSize, subStepSize)
136
-
137
- // Draw StepValue
138
- for (let num = 1; num <= count; num++) {
139
- if (num % step == 0 || num % subStep != 0) {
140
- // 메인 스탭과 서브 스탭은 그리지 않음
141
- continue
142
- }
143
- let locate = (width / count) * num
144
- context.fillRect(locate, height - subStepSize, stepNeedleSize, subStepSize)
145
- }
146
- }
147
-
148
- //// 스텝 텍스트 그리기 ////
149
- let fontSize = (width * stepTextSize) / 150
150
- context.fillStyle = textFillStyle
151
- context.font = fontSize + 'px arial'
152
- context.textBaseline = 'middle'
153
- context.textAlign = 'center'
154
- if (showStartValue) {
155
- // Draw StartText
156
- context.fillText(startValue, 0, height + fontSize * 0.75)
157
- }
158
-
159
- if (showEndValue) {
160
- // Draw EndText
161
- context.fillText(endValue, width, height + fontSize * 0.75)
162
- }
163
-
164
- if (showStepText) {
165
- // Draw StepText
166
- let count = totalValue / step
167
-
168
- for (let num = 1; num < count; num++) {
169
- let value = startValue + step * num
170
- let locate = (width / count) * num
171
-
172
- context.fillText(value, locate, height + fontSize * 0.75)
173
- }
174
- }
175
-
176
- //// 바늘 그리기 ////
177
- context.beginPath()
178
- let drawingValue = this.animValue
179
- drawingValue = Math.max(Math.min(drawingValue, endValue), startValue) // 그려지는 값은 startValue보다 작을 수 없고, endValue보다 클 수 없음.
180
- let position = ((drawingValue - startValue) / totalValue) * width
181
-
182
- needleSize *= 4
183
- context.moveTo(position, height + fontSize * 1.4)
184
- context.lineTo(position + needleSize / 2, height + needleSize + fontSize * 1.4)
185
- context.lineTo(position - needleSize / 2, height + needleSize + fontSize * 1.4)
186
-
187
- context.fillStyle = needleFillStyle
188
- context.fill()
189
- context.closePath()
190
-
191
- context.translate(-left, -top)
192
- }
193
-
194
- postrender(context: CanvasRenderingContext2D) {
195
- this.drawText(context)
196
- }
197
-
198
- get nature() {
199
- return NATURE
200
- }
201
- }
202
-
203
- Component.register('gauge-horizon', GaugeHorizon)
@@ -1,114 +0,0 @@
1
- /*
2
- * Copyright © HatioLab Inc. All rights reserved.
3
- */
4
-
5
- export const PROPERTIES = [
6
- {
7
- type: 'string',
8
- label: 'value',
9
- name: 'value',
10
- property: 'value'
11
- },
12
- {
13
- type: 'number',
14
- label: 'start-value',
15
- name: 'startValue',
16
- observe: function (this: HTMLElement, startValue: number) {
17
- const colorStops: any = this.parentNode!.querySelector('[name=colorStops]')
18
- colorStops.property.min = startValue
19
- },
20
- property: 'startValue'
21
- },
22
- {
23
- type: 'number',
24
- label: 'end-value',
25
- name: 'endValue',
26
- observe: function (this: HTMLElement, endValue: number) {
27
- const colorStops: any = this.parentNode!.querySelector('[name=colorStops]')
28
- colorStops.property.max = endValue
29
- },
30
- property: 'endValue'
31
- },
32
- {
33
- type: 'number',
34
- label: 'step',
35
- name: 'step',
36
- property: 'step'
37
- },
38
- {
39
- type: 'number',
40
- label: 'step-text-size',
41
- name: 'stepTextSize',
42
- property: 'stepTextSize'
43
- },
44
- {
45
- type: 'number',
46
- label: 'sub-step',
47
- name: 'subStep',
48
- property: 'subStep'
49
- },
50
- {
51
- type: 'number',
52
- label: 'step-needle-size',
53
- name: 'stepNeedleSize',
54
- property: 'stepNeedleSize'
55
- },
56
- {
57
- type: 'color',
58
- label: 'step-fill-style',
59
- name: 'stepFillStyle',
60
- property: 'stepFillStyle'
61
- },
62
- {
63
- type: 'solid-color-stops',
64
- label: 'color-stops',
65
- name: 'colorStops',
66
- property: {
67
- min: 0,
68
- max: 100
69
- }
70
- },
71
- {
72
- type: 'legend',
73
- label: '',
74
- name: 'toggleOption',
75
- property: {
76
- label: 'Toggle Option'
77
- }
78
- },
79
- {
80
- type: 'checkbox',
81
- label: 'show-start-value',
82
- name: 'showStartValue',
83
- property: 'showStartValue'
84
- },
85
- {
86
- type: 'checkbox',
87
- label: 'show-end-value',
88
- name: 'showEndValue',
89
- property: 'showEndValue'
90
- },
91
- {
92
- type: 'checkbox',
93
- label: 'show-step-line',
94
- name: 'showStepLine',
95
- property: 'showStepLine'
96
- },
97
- {
98
- type: 'checkbox',
99
- label: 'show-step-text',
100
- name: 'showStepText',
101
- property: 'showStepText'
102
- },
103
- {
104
- type: 'checkbox',
105
- label: 'show-sub-step',
106
- name: 'showSubStep',
107
- property: 'showSubStep'
108
- },
109
- {
110
- type: 'checkbox',
111
- label: 'anim-from-base',
112
- name: 'animFromBase'
113
- }
114
- ]