@ray-js/ipc-player-integration 0.0.49 → 0.0.50-beta.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/lib/ctx/ctx.composition.js +6 -1
- package/lib/ctx/ctx.js +11 -1
- package/lib/features/initPlayerWidgets/index.d.ts +8 -0
- package/lib/features/initPlayerWidgets/index.js +13 -0
- package/lib/i18n/index.d.ts +8 -0
- package/lib/i18n/strings.d.ts +4 -0
- package/lib/i18n/strings.js +4 -0
- package/lib/interface.d.ts +9 -1
- package/lib/ui/bottomLeftContent.js +23 -8
- package/lib/ui/constant.d.ts +10 -0
- package/lib/ui/constant.js +12 -1
- package/lib/ui/ui.js +78 -4
- package/lib/ui/ui.less +15 -0
- package/lib/utils/content/dpCode.d.ts +1 -0
- package/lib/utils/content/dpCode.js +5 -1
- package/lib/utils/storage/index.d.ts +2 -2
- package/lib/widgets/index.d.ts +1 -0
- package/lib/widgets/index.js +2 -1
- package/lib/widgets/realTimeMagnification/realTimeMagnification.js +18 -1
- package/lib/widgets/toggleVerticalFull/toggleVerticalFull.less +1 -1
- package/lib/widgets/zoomTurntable/constants.d.ts +92 -0
- package/lib/widgets/zoomTurntable/constants.js +148 -0
- package/lib/widgets/zoomTurntable/entryAnchor.d.ts +7 -0
- package/lib/widgets/zoomTurntable/entryAnchor.js +7 -0
- package/lib/widgets/zoomTurntable/index.d.ts +8 -0
- package/lib/widgets/zoomTurntable/index.js +8 -0
- package/lib/widgets/zoomTurntable/lensModel.d.ts +53 -0
- package/lib/widgets/zoomTurntable/lensModel.js +137 -0
- package/lib/widgets/zoomTurntable/lensModel.test.d.ts +1 -0
- package/lib/widgets/zoomTurntable/lensModel.test.js +336 -0
- package/lib/widgets/zoomTurntable/lensSkill.d.ts +89 -0
- package/lib/widgets/zoomTurntable/lensSkill.js +332 -0
- package/lib/widgets/zoomTurntable/measureVisible.d.ts +22 -0
- package/lib/widgets/zoomTurntable/measureVisible.js +76 -0
- package/lib/widgets/zoomTurntable/turntable/index.d.ts +10 -0
- package/lib/widgets/zoomTurntable/turntable/index.js +76 -0
- package/lib/widgets/zoomTurntable/turntable/index.less +11 -0
- package/lib/widgets/zoomTurntable/turntable/rjs/index.js +242 -0
- package/lib/widgets/zoomTurntable/turntable/rjs/index.json +3 -0
- package/lib/widgets/zoomTurntable/turntable/rjs/index.rjs +691 -0
- package/lib/widgets/zoomTurntable/turntable/rjs/index.tyml +7 -0
- package/lib/widgets/zoomTurntable/turntable/type.d.ts +95 -0
- package/lib/widgets/zoomTurntable/turntable/type.js +1 -0
- package/lib/widgets/zoomTurntable/useLensZoom.d.ts +37 -0
- package/lib/widgets/zoomTurntable/useLensZoom.js +418 -0
- package/lib/widgets/zoomTurntable/zoomTurntableDock.d.ts +17 -0
- package/lib/widgets/zoomTurntable/zoomTurntableDock.js +228 -0
- package/lib/widgets/zoomTurntable/zoomTurntableDock.less +11 -0
- package/lib/widgets/zoomTurntable/zoomTurntableEntry.d.ts +21 -0
- package/lib/widgets/zoomTurntable/zoomTurntableEntry.js +253 -0
- package/lib/widgets/zoomTurntable/zoomTurntableEntry.less +51 -0
- package/lib/widgets/zoomTurntable/zoomTurntableTip.d.ts +16 -0
- package/lib/widgets/zoomTurntable/zoomTurntableTip.js +76 -0
- package/lib/widgets/zoomTurntable/zoomTurntableTip.less +56 -0
- package/package.json +3 -3
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
// 手势 / 初始化链路日志开关。定位手势问题期间打开,稳定后置 false。
|
|
2
|
+
const DEBUG_GESTURE = true;
|
|
3
|
+
function dlog() {
|
|
4
|
+
if (!DEBUG_GESTURE) return;
|
|
5
|
+
var args = Array.prototype.slice.call(arguments);
|
|
6
|
+
console.log.apply(console, ['[zoomTurntable/rjs]'].concat(args));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const DEG_TO_RAD = Math.PI / 180;
|
|
10
|
+
const RAD_TO_DEG = 180 / Math.PI;
|
|
11
|
+
// 设计稿椭圆基准尺寸,纵横比固定 516:351;后面所有比例都以这个基准缩放。
|
|
12
|
+
const BASE_RX = 258; // 516 / 2
|
|
13
|
+
const BASE_RY = 175.5; // 351 / 2
|
|
14
|
+
const START_ANGLE = 180;
|
|
15
|
+
const END_ANGLE = 360;
|
|
16
|
+
const ARC_SPAN = END_ANGLE - START_ANGLE;
|
|
17
|
+
// 指针固定在弧线中点(顶部)
|
|
18
|
+
const POINTER_ANGLE = START_ANGLE + ARC_SPAN / 2;
|
|
19
|
+
// 轨道半径是遮罩半径的 5/6,整体比遮罩稍收一点
|
|
20
|
+
const TRACK_TO_MASK_RATIO = 5/6;
|
|
21
|
+
// 遮罩本身也保持同一椭圆比例
|
|
22
|
+
const MASK_RX_RATIO = 1;
|
|
23
|
+
const MASK_RY_RATIO = 1;
|
|
24
|
+
// 当前光标圆心实心圆半径固定,不开放外部配置,避免不同挂载层下传参表现不一致。
|
|
25
|
+
const POINTER_DOT_RADIUS = 3;
|
|
26
|
+
// 主刻度之间的次刻度段数(沿弧线均匀分布)
|
|
27
|
+
const MINOR_DIVS = 60;
|
|
28
|
+
// 次刻度靠近主刻度时的跳过阈值(progress 单位)
|
|
29
|
+
const MAJOR_SKIP = 0.004;
|
|
30
|
+
const LABEL_WINDOW = 0.28;
|
|
31
|
+
const TOP_SAFE_AREA = 50;
|
|
32
|
+
// 拖动方向:-1 表示「拖动的是转盘本身」——向左拖,盘面左移,右侧的大刻度转到指针处,倍率变大,
|
|
33
|
+
// 与实体相机的调焦环手感一致;+1 则是「拖动指针」的反向手感。
|
|
34
|
+
const DRAG_DIRECTION = -1;
|
|
35
|
+
|
|
36
|
+
class ZoomTurntableRenderer {
|
|
37
|
+
constructor(options) {
|
|
38
|
+
this.canvas = options.canvas;
|
|
39
|
+
this.ctx = this.canvas.getContext('2d');
|
|
40
|
+
this.min = options.min != null ? options.min : 0.6;
|
|
41
|
+
this.max = options.max != null ? options.max : 20;
|
|
42
|
+
this.step = options.step != null ? options.step : 0.1;
|
|
43
|
+
this.marks = options.marks || [];
|
|
44
|
+
this.ticks = options.ticks || [];
|
|
45
|
+
this.curve = options.curve || 'log';
|
|
46
|
+
// 读数小数位按 step 推导(step=0.3 → 1 位,step=0.25 → 2 位,step=1 → 0 位),
|
|
47
|
+
// 保证显示精度与可调精度一致。不开放外部函数注入(跨线程传函数需 new Function 还原,
|
|
48
|
+
// 有兼容与安全风险)。
|
|
49
|
+
var self = this;
|
|
50
|
+
this.formatLabel = function (v) { return v.toFixed(self._valueDecimals()) + 'X'; };
|
|
51
|
+
this.activeColor = options.activeColor || '#FFFFFF';
|
|
52
|
+
this.styleOptions = options.styleOptions || {};
|
|
53
|
+
this.orientation = this._normalizeOrientation(options.orientation);
|
|
54
|
+
this.cropRatio = options.cropRatio != null
|
|
55
|
+
? options.cropRatio
|
|
56
|
+
: this._style('cropRatio', 0.2);
|
|
57
|
+
this.callMethod = options.callMethod;
|
|
58
|
+
|
|
59
|
+
this.value = options.defaultValue != null ? options.defaultValue : this.min;
|
|
60
|
+
// progress(0~1) 是拖动时的唯一真相,保持浮点,避免 step 量化造成的抖动
|
|
61
|
+
this.progress = this.getProgress(this.value);
|
|
62
|
+
this.dragging = false;
|
|
63
|
+
this._rafScheduled = false;
|
|
64
|
+
this._rafId = null;
|
|
65
|
+
// 缓存的刻度几何(仅依赖 min/max/curve/marks,不依赖当前值)
|
|
66
|
+
this._majorTicks = [];
|
|
67
|
+
this._minorTicks = [];
|
|
68
|
+
this._markProgress = [];
|
|
69
|
+
|
|
70
|
+
// 按目标 scale 派生所有几何尺寸;这里先把“完整遮罩”的尺寸算出来,再按 cropRatio 截掉底部。
|
|
71
|
+
this._computeGeometry(options.scale != null ? options.scale : 1);
|
|
72
|
+
this._setupCanvasSize();
|
|
73
|
+
this._buildTicks();
|
|
74
|
+
this._render();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 按 scale 整体缩放,先算遮罩,再让轨道按遮罩比例内收。
|
|
78
|
+
// 轨道和遮罩共用圆心,顶部基准仍由 topSafeArea 控制,角度类(START_ANGLE/POINTER_ANGLE 等)不随尺寸变化。
|
|
79
|
+
_computeGeometry(scale) {
|
|
80
|
+
var inputScale = scale != null ? scale : 1; // 外部传入的整体缩放倍数
|
|
81
|
+
var cropRatio = this.cropRatio != null ? this.cropRatio : this._style('cropRatio', 0.2); // 从上弧顶截到多深
|
|
82
|
+
if (cropRatio < 0) cropRatio = 0;
|
|
83
|
+
if (cropRatio > 1) cropRatio = 1;
|
|
84
|
+
this.cropRatio = cropRatio;
|
|
85
|
+
|
|
86
|
+
var bottomSin = 2 * cropRatio - 1; // 对应截面底边的 sin 值,用来反推可见宽度
|
|
87
|
+
if (bottomSin < -1) bottomSin = -1;
|
|
88
|
+
if (bottomSin > 1) bottomSin = 1;
|
|
89
|
+
var cropWidthRatio = 1; // 截面底边相对完整椭圆宽度的比例
|
|
90
|
+
if (cropRatio < 0.5) {
|
|
91
|
+
cropWidthRatio = Math.sqrt(1 - bottomSin * bottomSin);
|
|
92
|
+
}
|
|
93
|
+
var contentScale = inputScale; // 最终用于整套几何的实际缩放值
|
|
94
|
+
|
|
95
|
+
this.inputScale = inputScale;
|
|
96
|
+
this.scale = contentScale;
|
|
97
|
+
this.scaleX = contentScale;
|
|
98
|
+
this.scaleY = contentScale;
|
|
99
|
+
this.maskRx = BASE_RX * contentScale * MASK_RX_RATIO;
|
|
100
|
+
this.maskRy = BASE_RY * contentScale * MASK_RY_RATIO;
|
|
101
|
+
// 轨道(含刻度点、指针)与标记文字整体向椭圆内侧收,加大它们与遮罩边缘的距离。
|
|
102
|
+
// 遮罩半径不变,所以 canvas 尺寸与截面都不受影响。
|
|
103
|
+
this.trackEdgeInset = this._style('trackEdgeInset', 0) * contentScale;
|
|
104
|
+
this.rx = this.maskRx * TRACK_TO_MASK_RATIO - this.trackEdgeInset;
|
|
105
|
+
this.ry = this.maskRy * TRACK_TO_MASK_RATIO - this.trackEdgeInset;
|
|
106
|
+
var fullWidth = this.maskRx * 2; // 完整椭圆的理论宽度
|
|
107
|
+
this.CX = fullWidth / 2; // 椭圆在完整坐标系里水平居中
|
|
108
|
+
this.topSafeArea = this._style('topSafeArea', TOP_SAFE_AREA) * contentScale;
|
|
109
|
+
this.CY = this.maskRy + this.topSafeArea; // 顶部预留当前倍率文字空间
|
|
110
|
+
// cropRatio 表示“从上弧顶往下截取到哪里”,0 是最上弧顶,1 是完整椭圆底部。
|
|
111
|
+
this.cropBottom = this.topSafeArea + this.maskRy * 2 * cropRatio;
|
|
112
|
+
|
|
113
|
+
var cropHalfWidth = this.maskRx; // 当前截面可见半宽,默认等于完整椭圆半径
|
|
114
|
+
this.maskVisibleStartAngle = START_ANGLE;
|
|
115
|
+
this.maskVisibleEndAngle = END_ANGLE;
|
|
116
|
+
if (cropRatio < 0.5) {
|
|
117
|
+
var bottomAngle = Math.asin(bottomSin) * RAD_TO_DEG; // 截面底边对应的角度
|
|
118
|
+
this.maskVisibleStartAngle = 180 - bottomAngle;
|
|
119
|
+
this.maskVisibleEndAngle = 360 + bottomAngle;
|
|
120
|
+
cropHalfWidth = this.maskRx * Math.cos(bottomAngle * DEG_TO_RAD);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 绘制内容整体纵向偏移(正值下移)。只平移画布坐标系,
|
|
124
|
+
// 不参与任何角度 / 半径计算,因此刻度与文案的角度分布保持原样。
|
|
125
|
+
this.contentOffsetY = this._style('contentOffsetY', 0) * contentScale;
|
|
126
|
+
|
|
127
|
+
// 触摸响应余量:canvas 是唯一能收 touch 的元素,若尺寸严格等于椭圆截面,
|
|
128
|
+
// 椭圆上方 / 两侧的空白区域就滑不动(观感上「只有中间能滑」)。
|
|
129
|
+
// 这里把画布向外扩一圈,绘制坐标同步补偿,视觉不变但整个转盘区域都可拖动。
|
|
130
|
+
this.hitPaddingX = this._style('hitPaddingX', 0) * contentScale;
|
|
131
|
+
this.hitPaddingY = this._style('hitPaddingY', 0) * contentScale;
|
|
132
|
+
|
|
133
|
+
this.viewportLeft = this.CX - cropHalfWidth;
|
|
134
|
+
this.viewportTop = this.topSafeArea;
|
|
135
|
+
this.viewportRight = this.CX + cropHalfWidth;
|
|
136
|
+
this.viewportBottom = this.cropBottom;
|
|
137
|
+
// 组件实际占位只取当前截面的真实宽度/高度,避免外层多出空白。
|
|
138
|
+
this.drawWidth = this.viewportRight - this.viewportLeft;
|
|
139
|
+
this.drawHeight = this.viewportBottom - this.viewportTop;
|
|
140
|
+
// 画布尺寸 = 椭圆截面 + 触摸响应余量(两侧 / 上下各扩一份)
|
|
141
|
+
this.baseWidth = this.drawWidth + this.hitPaddingX * 2;
|
|
142
|
+
this.baseHeight = this.drawHeight + this.hitPaddingY * 2;
|
|
143
|
+
// 横屏侧边展示时,绘制坐标系先整体旋转 90 度,所以 canvas 宽高也要交换。
|
|
144
|
+
this.WIDTH = this.orientation === 'right' ? this.baseHeight : this.baseWidth;
|
|
145
|
+
this.HEIGHT = this.orientation === 'right' ? this.baseWidth : this.baseHeight;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
_setupCanvasSize() {
|
|
149
|
+
const systemInfo = getSystemInfo();
|
|
150
|
+
const dpr = systemInfo.pixelRatio || 1;
|
|
151
|
+
// 这里设置的是 canvas 物理像素尺寸;style 里保留逻辑像素尺寸,避免模糊。
|
|
152
|
+
this.canvas.width = this.WIDTH * dpr;
|
|
153
|
+
this.canvas.height = this.HEIGHT * dpr;
|
|
154
|
+
this.canvas.style.width = this.WIDTH + 'px';
|
|
155
|
+
this.canvas.style.height = this.HEIGHT + 'px';
|
|
156
|
+
if (this.ctx.setTransform) {
|
|
157
|
+
this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
158
|
+
} else {
|
|
159
|
+
this.ctx.scale(dpr, dpr);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
getProgress(v) {
|
|
164
|
+
var clamped = Math.max(this.min, Math.min(this.max, v));
|
|
165
|
+
if (this.curve === 'log') {
|
|
166
|
+
return Math.log(clamped / this.min) / Math.log(this.max / this.min);
|
|
167
|
+
}
|
|
168
|
+
var f = typeof this.curve === 'number' ? this.curve : 1;
|
|
169
|
+
return Math.pow((clamped - this.min) / (this.max - this.min), 1 / f);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// progress(0~1) -> 实际倍率值(未量化)
|
|
173
|
+
_valueFromProgress(p) {
|
|
174
|
+
var clamped = Math.max(0, Math.min(1, p));
|
|
175
|
+
if (this.curve === 'log') {
|
|
176
|
+
return this.min * Math.pow(this.max / this.min, clamped);
|
|
177
|
+
}
|
|
178
|
+
var f = typeof this.curve === 'number' ? this.curve : 1;
|
|
179
|
+
return this.min + (this.max - this.min) * Math.pow(clamped, f);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// 按 step 量化,并夹到 [min, max]
|
|
183
|
+
_snapValue(raw) {
|
|
184
|
+
var snapped = Math.round(raw / this.step) * this.step;
|
|
185
|
+
if (snapped < this.min) return this.min;
|
|
186
|
+
if (snapped > this.max) return this.max;
|
|
187
|
+
return snapped;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 刻度盘上的角度(相对于圆心)。
|
|
191
|
+
// progress = 0 对应左端,progress = 1 对应右端,整段弧线线性映射。
|
|
192
|
+
getAngle(p) {
|
|
193
|
+
return START_ANGLE + p * ARC_SPAN;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 预计算刻度几何。主刻度画在 mark 的精确位置,次刻度沿 progress 均匀分布,
|
|
197
|
+
// 靠近主刻度的次刻度跳过 —— 这样刻度线与数字标签严格对齐。
|
|
198
|
+
_buildTicks() {
|
|
199
|
+
var marks = this.marks || [];
|
|
200
|
+
var markProgress = [];
|
|
201
|
+
var majorTicks = [];
|
|
202
|
+
for (var i = 0; i < marks.length; i++) {
|
|
203
|
+
var p = this.getProgress(marks[i].value);
|
|
204
|
+
markProgress.push(p);
|
|
205
|
+
majorTicks.push(this.getAngle(p) * DEG_TO_RAD);
|
|
206
|
+
}
|
|
207
|
+
this._markProgress = markProgress;
|
|
208
|
+
this._majorTicks = majorTicks;
|
|
209
|
+
|
|
210
|
+
var minorTicks = [];
|
|
211
|
+
var isNearMajor = function (pp) {
|
|
212
|
+
for (var m = 0; m < markProgress.length; m++) {
|
|
213
|
+
if (Math.abs(markProgress[m] - pp) < MAJOR_SKIP) return true;
|
|
214
|
+
}
|
|
215
|
+
return false;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
if (this.ticks && this.ticks.length) {
|
|
219
|
+
// 外部给定刻度值:各镜头区间按自己的 step 生成,间距逐段不同
|
|
220
|
+
for (var t = 0; t < this.ticks.length; t++) {
|
|
221
|
+
var tp = this.getProgress(this.ticks[t]);
|
|
222
|
+
if (isNearMajor(tp)) continue;
|
|
223
|
+
minorTicks.push(this.getAngle(tp) * DEG_TO_RAD);
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
// 兜底:沿 progress 均匀分布
|
|
227
|
+
for (var j = 0; j <= MINOR_DIVS; j++) {
|
|
228
|
+
var pp = j / MINOR_DIVS;
|
|
229
|
+
if (isNearMajor(pp)) continue;
|
|
230
|
+
minorTicks.push(this.getAngle(pp) * DEG_TO_RAD);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
this._minorTicks = minorTicks;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// 用 rAF 合并多次重绘请求,避免每个 touchmove 事件都全量重绘
|
|
237
|
+
_scheduleRender() {
|
|
238
|
+
if (this._rafScheduled) return;
|
|
239
|
+
this._rafScheduled = true;
|
|
240
|
+
var self = this;
|
|
241
|
+
var raf = this._getRaf();
|
|
242
|
+
this._rafId = raf(function () {
|
|
243
|
+
self._rafScheduled = false;
|
|
244
|
+
self._rafId = null;
|
|
245
|
+
self._render();
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
_getRaf() {
|
|
250
|
+
if (this.canvas && typeof this.canvas.requestAnimationFrame === 'function') {
|
|
251
|
+
return this.canvas.requestAnimationFrame.bind(this.canvas);
|
|
252
|
+
}
|
|
253
|
+
if (typeof requestAnimationFrame === 'function') {
|
|
254
|
+
return requestAnimationFrame;
|
|
255
|
+
}
|
|
256
|
+
return function (fn) { return setTimeout(fn, 16); };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
_render() {
|
|
260
|
+
var ctx = this.ctx;
|
|
261
|
+
var s = this.scale;
|
|
262
|
+
var geo = s * TRACK_TO_MASK_RATIO;
|
|
263
|
+
ctx.clearRect(0, 0, this.WIDTH, this.HEIGHT);
|
|
264
|
+
ctx.save();
|
|
265
|
+
if (this.orientation === 'right') {
|
|
266
|
+
this._textCounterRotation = Math.PI / 2;
|
|
267
|
+
ctx.translate(0, this.baseWidth);
|
|
268
|
+
ctx.rotate(-Math.PI / 2);
|
|
269
|
+
} else {
|
|
270
|
+
this._textCounterRotation = 0;
|
|
271
|
+
}
|
|
272
|
+
// 画布因触摸余量向外扩过,绘制原点要补偿回来,保证视觉位置不变
|
|
273
|
+
ctx.translate(
|
|
274
|
+
-this.viewportLeft + (this.hitPaddingX || 0),
|
|
275
|
+
-this.viewportTop + (this.hitPaddingY || 0) + (this.contentOffsetY || 0)
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
// 刻度盘角度位移:让当前 progress 对应的刻度转到指针处。
|
|
279
|
+
// 给每个刻度的参数角度直接加上位移,再用椭圆参数方程算坐标,刻度始终落在椭圆弧上。
|
|
280
|
+
var rotation = POINTER_ANGLE - this.getAngle(this.progress); // 当前值对应的整体旋转角
|
|
281
|
+
var rotRad = rotation * DEG_TO_RAD; // rotation 的弧度值
|
|
282
|
+
var visibleStart = Math.max(START_ANGLE, START_ANGLE + rotation); // 当前可见弧段起点
|
|
283
|
+
var visibleEnd = Math.min(END_ANGLE, END_ANGLE + rotation); // 当前可见弧段终点
|
|
284
|
+
if (visibleEnd <= visibleStart) {
|
|
285
|
+
ctx.restore();
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// 半透明扇形遮罩:固定成完整扇面,不跟随刻度旋转;
|
|
290
|
+
// 这样“被截掉的底边”始终是组件视觉底部,外部只看到截面。
|
|
291
|
+
ctx.beginPath();
|
|
292
|
+
ctx.moveTo(
|
|
293
|
+
this.CX + this.maskRx * Math.cos(this.maskVisibleStartAngle * DEG_TO_RAD),
|
|
294
|
+
this.CY + this.maskRy * Math.sin(this.maskVisibleStartAngle * DEG_TO_RAD)
|
|
295
|
+
);
|
|
296
|
+
ctx.ellipse(
|
|
297
|
+
this.CX,
|
|
298
|
+
this.CY,
|
|
299
|
+
this.maskRx,
|
|
300
|
+
this.maskRy,
|
|
301
|
+
0,
|
|
302
|
+
this.maskVisibleStartAngle * DEG_TO_RAD,
|
|
303
|
+
this.maskVisibleEndAngle * DEG_TO_RAD,
|
|
304
|
+
false
|
|
305
|
+
);
|
|
306
|
+
ctx.lineTo(this.viewportRight, this.viewportBottom);
|
|
307
|
+
ctx.lineTo(this.viewportLeft, this.viewportBottom);
|
|
308
|
+
ctx.closePath();
|
|
309
|
+
ctx.fillStyle = 'rgba(0,0,0,' + this._style('overlayOpacity', 0.44) + ')';
|
|
310
|
+
ctx.fill();
|
|
311
|
+
|
|
312
|
+
// 参考图里的轨道是柔和虚线弧,不是密集刻度。
|
|
313
|
+
ctx.save();
|
|
314
|
+
ctx.beginPath();
|
|
315
|
+
ctx.ellipse(this.CX, this.CY, this.rx, this.ry, 0, visibleStart * DEG_TO_RAD, visibleEnd * DEG_TO_RAD, false);
|
|
316
|
+
ctx.setLineDash([this._style('dashLength', 8) * geo, this._style('dashGap', 8) * geo]);
|
|
317
|
+
ctx.lineDashOffset = -3 * geo;
|
|
318
|
+
ctx.strokeStyle = 'rgba(255,255,255,0.42)';
|
|
319
|
+
ctx.lineWidth = this._style('trackLineWidth', 2.2) * geo;
|
|
320
|
+
ctx.lineCap = 'round';
|
|
321
|
+
ctx.stroke();
|
|
322
|
+
ctx.restore();
|
|
323
|
+
|
|
324
|
+
// 主刻度点和附近标签。只展示指针附近的标签,避免默认多焦段标记铺满弧线。
|
|
325
|
+
var dotRx = this.rx; // 刻度点所在椭圆的横半径
|
|
326
|
+
var dotRy = this.ry; // 刻度点所在椭圆的纵半径
|
|
327
|
+
var isRightOrientation = this.orientation === 'right'; // 是否为横屏侧边模式
|
|
328
|
+
// 横屏侧边模式时,标签朝椭圆内侧收;底部模式则往外侧展开一点,保证和弧线拉开距离。
|
|
329
|
+
var labelRx = isRightOrientation
|
|
330
|
+
? this.rx - this._style('innerLabelOffsetX', 22) * geo
|
|
331
|
+
: this.rx + 4 * geo;
|
|
332
|
+
var labelRy = isRightOrientation
|
|
333
|
+
? this.ry - this._style('innerLabelOffsetY', 22) * geo
|
|
334
|
+
: this.ry + 18 * geo;
|
|
335
|
+
var marks = this.marks || [];
|
|
336
|
+
if (marks.length > 0) {
|
|
337
|
+
ctx.textAlign = 'center';
|
|
338
|
+
ctx.textBaseline = 'middle';
|
|
339
|
+
for (var k = 0; k < marks.length; k++) {
|
|
340
|
+
var progressOffset = this._markProgress[k] - this.progress; // 当前 mark 距离指针的相对位置
|
|
341
|
+
if (Math.abs(progressOffset) > LABEL_WINDOW) continue;
|
|
342
|
+
if (Math.abs(progressOffset) < 0.012) continue;
|
|
343
|
+
var mRad = this._majorTicks[k] + rotRad; // 该 mark 旋转后的弧度角
|
|
344
|
+
var mDeg = mRad / DEG_TO_RAD; // 方便和 visibleStart / visibleEnd 比较
|
|
345
|
+
if (mDeg < visibleStart || mDeg > visibleEnd) continue;
|
|
346
|
+
var dx = this.CX + dotRx * Math.cos(mRad);
|
|
347
|
+
var dy = this.CY + dotRy * Math.sin(mRad);
|
|
348
|
+
ctx.beginPath();
|
|
349
|
+
ctx.arc(dx, dy, this._style('markDotRadius', 3) * geo, 0, Math.PI * 2);
|
|
350
|
+
ctx.fillStyle = 'rgba(255,255,255,0.62)';
|
|
351
|
+
ctx.fill();
|
|
352
|
+
|
|
353
|
+
var lx = this.CX + labelRx * Math.cos(mRad); // 刻度文字 x 坐标
|
|
354
|
+
var ly = this.CY + labelRy * Math.sin(mRad); // 刻度文字 y 坐标
|
|
355
|
+
ctx.fillStyle = 'rgba(255,255,255,' + (0.76 - Math.abs(progressOffset) * 1.5) + ')';
|
|
356
|
+
ctx.font = '500 ' + (this._style('markLabelFontSize', 14) * s) + 'px sans-serif';
|
|
357
|
+
this._fillText(this._normalizeLabel(marks[k].label, marks[k].value), lx, ly - 2 * s);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// 固定中心指示器:白色圆环 + 当前倍率,形成参考图里的视觉锚点。
|
|
362
|
+
var pointerRad = POINTER_ANGLE * DEG_TO_RAD; // 指针在椭圆上的固定角度
|
|
363
|
+
var px = this.CX + this.rx * Math.cos(pointerRad); // 指针圆环中心 x
|
|
364
|
+
var py = this.CY + this.ry * Math.sin(pointerRad); // 指针圆环中心 y
|
|
365
|
+
// 当前值文字和普通刻度标签共用同一套 labelRx/labelRy,保证位置、字号和节奏一致。
|
|
366
|
+
var currentLabelRx = labelRx;
|
|
367
|
+
// 横屏右侧时当前值文字再往内侧让一点,避免和固定光标圆环重合。
|
|
368
|
+
var currentLabelRy = isRightOrientation ? labelRy - 10 * geo : labelRy;
|
|
369
|
+
var currentLabelX = this.CX + currentLabelRx * Math.cos(pointerRad); // 当前值文字 x
|
|
370
|
+
var currentLabelY = this.CY + currentLabelRy * Math.sin(pointerRad); // 当前值文字 y
|
|
371
|
+
ctx.beginPath();
|
|
372
|
+
ctx.arc(px, py, this._style('pointerRadius', 6) * geo, 0, Math.PI * 2);
|
|
373
|
+
ctx.strokeStyle = this.activeColor;
|
|
374
|
+
ctx.lineWidth = this._style('pointerStrokeWidth', 1.5) * geo;
|
|
375
|
+
ctx.stroke();
|
|
376
|
+
ctx.beginPath();
|
|
377
|
+
ctx.arc(px, py, POINTER_DOT_RADIUS * geo, 0, Math.PI * 2);
|
|
378
|
+
ctx.fillStyle = this.activeColor;
|
|
379
|
+
ctx.fill();
|
|
380
|
+
|
|
381
|
+
ctx.textAlign = 'center';
|
|
382
|
+
ctx.textBaseline = 'middle';
|
|
383
|
+
ctx.fillStyle = this.activeColor;
|
|
384
|
+
ctx.font = '700 ' + (this._style('currentLabelFontSize', this._style('markLabelFontSize', 14)) * s) + 'px sans-serif';
|
|
385
|
+
this._fillText(this._formatValue(this.value), currentLabelX, currentLabelY - 2 * s);
|
|
386
|
+
ctx.restore();
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
_fillText(text, x, y) {
|
|
390
|
+
if (!this._textCounterRotation) {
|
|
391
|
+
this.ctx.fillText(text, x, y);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
this.ctx.save();
|
|
395
|
+
// 横屏侧边模式下,画布整体旋转过,文字需要反向旋回,才能保持横排可读。
|
|
396
|
+
this.ctx.translate(x, y);
|
|
397
|
+
this.ctx.rotate(this._textCounterRotation);
|
|
398
|
+
this.ctx.fillText(text, 0, 0);
|
|
399
|
+
this.ctx.restore();
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
_normalizeOrientation(orientation) {
|
|
403
|
+
return orientation === 'right' ? 'right' : 'bottom';
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
_style(name, fallback) {
|
|
407
|
+
var value = this.styleOptions && this.styleOptions[name];
|
|
408
|
+
if (value == null) return fallback;
|
|
409
|
+
var parsed = Number(value);
|
|
410
|
+
return isNaN(parsed) ? fallback : parsed;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// 当前 step 需要几位小数:0.3 → 1,0.25 → 2,1 → 0
|
|
414
|
+
_valueDecimals() {
|
|
415
|
+
var step = this.step != null ? this.step : 0.1;
|
|
416
|
+
var text = String(step);
|
|
417
|
+
var dot = text.indexOf('.');
|
|
418
|
+
if (dot < 0) return 0;
|
|
419
|
+
return Math.min(2, text.length - dot - 1);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
_formatValue(v) {
|
|
423
|
+
if (typeof this.formatLabel === 'function') return this._normalizeLabel(this.formatLabel(v), v);
|
|
424
|
+
return v.toFixed(this._valueDecimals()) + 'X';
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
_normalizeLabel(label, value) {
|
|
428
|
+
var decimals = this._valueDecimals();
|
|
429
|
+
var text = label != null ? String(label) : Number(value).toFixed(decimals);
|
|
430
|
+
if (!/[xX]$/.test(text)) {
|
|
431
|
+
var n = Number(text);
|
|
432
|
+
if (!isNaN(n)) text = n.toFixed(decimals);
|
|
433
|
+
text += 'X';
|
|
434
|
+
}
|
|
435
|
+
return text.replace(/x$/, 'X');
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
setValue(v) {
|
|
439
|
+
// 拖动中忽略外部回写,防止受控反馈循环导致闪动和方向反转
|
|
440
|
+
if (this.dragging) return;
|
|
441
|
+
// 松手后冷却期内忽略所有回写(含 onChange 残余),彻底打断反馈循环
|
|
442
|
+
if (this._cooldownUntil && Date.now() < this._cooldownUntil) return;
|
|
443
|
+
// 受控回写也按 step 量化:手势缩放回读来的是连续值(如 3.4666…),
|
|
444
|
+
// 不量化的话中间读数会出现非 step 倍数的数字,与拖动路径(_snapValue)不一致。
|
|
445
|
+
var snapped = this._snapValue(Math.max(this.min, Math.min(this.max, v)));
|
|
446
|
+
// 值无明显变化则跳过,避免浮点抖动触发多余重绘
|
|
447
|
+
if (Math.abs(snapped - this.value) < this.step * 0.1) return;
|
|
448
|
+
this.value = snapped;
|
|
449
|
+
this.progress = this.getProgress(this.value);
|
|
450
|
+
this._scheduleRender();
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
updateOptions(options) {
|
|
454
|
+
var prevScale = this.inputScale; // 上一次的整体缩放
|
|
455
|
+
var prevOrientation = this.orientation; // 上一次方向
|
|
456
|
+
var prevCropRatio = this.cropRatio; // 上一次截面比例
|
|
457
|
+
var prevTopSafeArea = this._style('topSafeArea', TOP_SAFE_AREA); // 上一次顶部安全区
|
|
458
|
+
var prevHitPaddingX = this._style('hitPaddingX', 0); // 上一次横向触摸余量
|
|
459
|
+
var prevHitPaddingY = this._style('hitPaddingY', 0); // 上一次纵向触摸余量
|
|
460
|
+
if (options.min != null) this.min = options.min;
|
|
461
|
+
if (options.max != null) this.max = options.max;
|
|
462
|
+
if (options.step != null) this.step = options.step;
|
|
463
|
+
if (options.marks != null) this.marks = options.marks;
|
|
464
|
+
if (options.ticks != null) this.ticks = options.ticks;
|
|
465
|
+
if (options.curve != null) this.curve = options.curve;
|
|
466
|
+
if (options.activeColor != null) this.activeColor = options.activeColor;
|
|
467
|
+
if (options.styleOptions != null) this.styleOptions = options.styleOptions;
|
|
468
|
+
if (options.orientation != null) this.orientation = this._normalizeOrientation(options.orientation);
|
|
469
|
+
if (options.cropRatio != null) this.cropRatio = options.cropRatio;
|
|
470
|
+
var nextScale = options.scale != null ? options.scale : prevScale; // 更新后的缩放
|
|
471
|
+
var nextTopSafeArea = this._style('topSafeArea', TOP_SAFE_AREA); // 更新后的顶部安全区
|
|
472
|
+
var shouldResetCanvas =
|
|
473
|
+
nextScale !== prevScale ||
|
|
474
|
+
this.orientation !== prevOrientation ||
|
|
475
|
+
this.cropRatio !== prevCropRatio ||
|
|
476
|
+
nextTopSafeArea !== prevTopSafeArea ||
|
|
477
|
+
this._style('hitPaddingX', 0) !== prevHitPaddingX ||
|
|
478
|
+
this._style('hitPaddingY', 0) !== prevHitPaddingY;
|
|
479
|
+
// 只有尺寸相关参数变化才重设 canvas;字号、线宽等普通样式变化只重绘,避免拖动时闪屏。
|
|
480
|
+
if (shouldResetCanvas) {
|
|
481
|
+
var nextScale = options.scale != null ? options.scale : this.inputScale;
|
|
482
|
+
this._computeGeometry(nextScale);
|
|
483
|
+
this._setupCanvasSize();
|
|
484
|
+
}
|
|
485
|
+
// 以下两项不改变 canvas 尺寸,走不到上面的重算分支,这里单独同步后重绘即可
|
|
486
|
+
this.contentOffsetY = this._style('contentOffsetY', 0) * this.scale;
|
|
487
|
+
this.trackEdgeInset = this._style('trackEdgeInset', 0) * this.scale;
|
|
488
|
+
this.rx = this.maskRx * TRACK_TO_MASK_RATIO - this.trackEdgeInset;
|
|
489
|
+
this.ry = this.maskRy * TRACK_TO_MASK_RATIO - this.trackEdgeInset;
|
|
490
|
+
this.value = Math.max(this.min, Math.min(this.max, this.value));
|
|
491
|
+
this.progress = this.getProgress(this.value);
|
|
492
|
+
this._buildTicks();
|
|
493
|
+
this._scheduleRender();
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
_handTouchStart(e) {
|
|
497
|
+
dlog('touchstart 触发', 'hasPreventDefault=' + !!e.preventDefault, 'touches=' + ((e.touches && e.touches.length) || 0));
|
|
498
|
+
if (e.preventDefault) e.preventDefault();
|
|
499
|
+
if (e.stopPropagation) e.stopPropagation();
|
|
500
|
+
this.dragging = true;
|
|
501
|
+
// 通知逻辑层禁用原生手势(ty.nativeDisabled),否则播放器的原生缩放/拖动会抢走手势
|
|
502
|
+
this.callMethod('onTouchStart');
|
|
503
|
+
var touch = e.touches && e.touches[0];
|
|
504
|
+
if (!touch) {
|
|
505
|
+
dlog('touchstart 无 touches,改用 changedTouches');
|
|
506
|
+
touch = e.changedTouches && e.changedTouches[0];
|
|
507
|
+
}
|
|
508
|
+
if (!touch) {
|
|
509
|
+
dlog('touchstart 拿不到 touch 点,事件对象 keys=', Object.keys(e || {}).join(','));
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
// 缓存 rect,避免每次 move 都调用;右侧模式用 y 轴位移模拟侧向滑动。
|
|
513
|
+
this._cachedRect = this.canvas.getBoundingClientRect
|
|
514
|
+
? this.canvas.getBoundingClientRect()
|
|
515
|
+
: null;
|
|
516
|
+
dlog(
|
|
517
|
+
'touchstart rect=',
|
|
518
|
+
this._cachedRect
|
|
519
|
+
? 'left=' + this._cachedRect.left + ' top=' + this._cachedRect.top +
|
|
520
|
+
' w=' + this._cachedRect.width + ' h=' + this._cachedRect.height
|
|
521
|
+
: 'null(getBoundingClientRect 不可用)',
|
|
522
|
+
'clientX=' + touch.clientX,
|
|
523
|
+
'clientY=' + touch.clientY
|
|
524
|
+
);
|
|
525
|
+
var rectLeft = this._cachedRect ? this._cachedRect.left : 0;
|
|
526
|
+
var rectTop = this._cachedRect ? this._cachedRect.top : 0;
|
|
527
|
+
this.lastTouchPoint = this.orientation === 'right'
|
|
528
|
+
? touch.clientY - rectTop
|
|
529
|
+
: touch.clientX - rectLeft;
|
|
530
|
+
dlog('touchstart lastTouchPoint=' + this.lastTouchPoint, 'value=' + this.value);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
_handTouchMove(e) {
|
|
534
|
+
if (!this.dragging) {
|
|
535
|
+
dlog('touchmove 被忽略:dragging=false(touchstart 未触发或已被 end/cancel 结束)');
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (e.preventDefault) e.preventDefault();
|
|
539
|
+
if (e.stopPropagation) e.stopPropagation();
|
|
540
|
+
var touch = (e.touches && e.touches[0]) || (e.changedTouches && e.changedTouches[0]);
|
|
541
|
+
if (!touch) {
|
|
542
|
+
dlog('touchmove 拿不到 touch 点');
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
// 使用缓存的 rect,不再重复调用 getBoundingClientRect
|
|
546
|
+
var rectLeft = this._cachedRect ? this._cachedRect.left : 0;
|
|
547
|
+
var rectTop = this._cachedRect ? this._cachedRect.top : 0;
|
|
548
|
+
var currentPoint = this.orientation === 'right'
|
|
549
|
+
? touch.clientY - rectTop
|
|
550
|
+
: touch.clientX - rectLeft;
|
|
551
|
+
|
|
552
|
+
// 像素增量直接映射到「值」的等量变化,而非 progress;
|
|
553
|
+
// 这样无论处于高倍率还是低倍率端,等量滑动产生等量值变化,手感一致(消除 log 曲线导致的两端灵敏度差异)。
|
|
554
|
+
var deltaX = (currentPoint - this.lastTouchPoint) * DRAG_DIRECTION; // 本次拖动的像素增量(含方向修正)
|
|
555
|
+
this.lastTouchPoint = currentPoint;
|
|
556
|
+
var valueSpan = this.max - this.min; // 整个倍率区间跨度
|
|
557
|
+
var pixelsForFullSpan = 300 * this.scale; // 走完整个区间大约需要多少像素
|
|
558
|
+
var rawValue = this._valueFromProgress(this.progress) + (deltaX / pixelsForFullSpan) * valueSpan; // 未量化的新值
|
|
559
|
+
if (rawValue < this.min) rawValue = this.min;
|
|
560
|
+
else if (rawValue > this.max) rawValue = this.max;
|
|
561
|
+
// 绘制位置和回调值使用同一个 step 量化结果,避免松手后受控值回写造成回滑。
|
|
562
|
+
var newValue = this._snapValue(rawValue); // 对齐 step 后的最终值
|
|
563
|
+
dlog(
|
|
564
|
+
'touchmove',
|
|
565
|
+
'currentPoint=' + currentPoint.toFixed(1),
|
|
566
|
+
'delta=' + deltaX.toFixed(1),
|
|
567
|
+
'span=' + valueSpan,
|
|
568
|
+
'pxForSpan=' + pixelsForFullSpan,
|
|
569
|
+
'raw=' + rawValue.toFixed(3),
|
|
570
|
+
'new=' + newValue.toFixed(2),
|
|
571
|
+
'old=' + this.value.toFixed(2),
|
|
572
|
+
newValue === this.value ? '(值未变化)' : '(值已更新)'
|
|
573
|
+
);
|
|
574
|
+
this.progress = this.getProgress(newValue);
|
|
575
|
+
if (newValue !== this.value) {
|
|
576
|
+
this.value = newValue;
|
|
577
|
+
this.callMethod('onChange', newValue);
|
|
578
|
+
}
|
|
579
|
+
this._scheduleRender();
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
_handTouchEnd() {
|
|
583
|
+
dlog('touchend/cancel 触发', 'dragging=' + this.dragging, 'value=' + this.value);
|
|
584
|
+
if (this.dragging) {
|
|
585
|
+
this.dragging = false;
|
|
586
|
+
this._cachedRect = null;
|
|
587
|
+
// 300ms 冷却期,吸收跨线程残余的 onChange 回写,避免松手后闪动
|
|
588
|
+
this._cooldownUntil = Date.now() + 300;
|
|
589
|
+
this.callMethod('onChangeEnd', this.value);
|
|
590
|
+
}
|
|
591
|
+
// 无论是否处于拖动,都要恢复原生手势,避免异常路径下 nativeDisabled 卡在 true
|
|
592
|
+
this.callMethod('onTouchEnd');
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
setupEventListeners() {
|
|
596
|
+
dlog(
|
|
597
|
+
'setupEventListeners',
|
|
598
|
+
'canvas=' + (this.canvas ? 'ok' : 'null'),
|
|
599
|
+
'addEventListener=' + typeof (this.canvas && this.canvas.addEventListener),
|
|
600
|
+
'WIDTH=' + this.WIDTH,
|
|
601
|
+
'HEIGHT=' + this.HEIGHT,
|
|
602
|
+
'orientation=' + this.orientation
|
|
603
|
+
);
|
|
604
|
+
if (!this.canvas || typeof this.canvas.addEventListener !== 'function') {
|
|
605
|
+
dlog('canvas 不支持 addEventListener,手势无法绑定');
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
this.handTouchStart = this._handTouchStart.bind(this);
|
|
609
|
+
this.handTouchMove = this._handTouchMove.bind(this);
|
|
610
|
+
this.handTouchEnd = this._handTouchEnd.bind(this);
|
|
611
|
+
this.canvas.addEventListener('touchstart', this.handTouchStart, false);
|
|
612
|
+
this.canvas.addEventListener('touchmove', this.handTouchMove, false);
|
|
613
|
+
this.canvas.addEventListener('touchend', this.handTouchEnd, false);
|
|
614
|
+
// 手势被系统打断时 touchend 不会派发,必须补 touchcancel,
|
|
615
|
+
// 否则 ty.nativeDisabled 会卡在 true,之后播放器所有原生手势都失效
|
|
616
|
+
this.canvas.addEventListener('touchcancel', this.handTouchEnd, false);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
destroy() {
|
|
620
|
+
if (this._rafId != null) {
|
|
621
|
+
var cancel = (this.canvas && this.canvas.cancelAnimationFrame)
|
|
622
|
+
|| (typeof cancelAnimationFrame === 'function' ? cancelAnimationFrame : clearTimeout);
|
|
623
|
+
try { cancel(this._rafId); } catch (e) {}
|
|
624
|
+
this._rafId = null;
|
|
625
|
+
}
|
|
626
|
+
if (this.canvas) {
|
|
627
|
+
this.canvas.removeEventListener('touchstart', this.handTouchStart, false);
|
|
628
|
+
this.canvas.removeEventListener('touchmove', this.handTouchMove, false);
|
|
629
|
+
this.canvas.removeEventListener('touchend', this.handTouchEnd, false);
|
|
630
|
+
this.canvas.removeEventListener('touchcancel', this.handTouchEnd, false);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
export default Render({
|
|
636
|
+
init(canvasId, options) {
|
|
637
|
+
this.canvasId = canvasId;
|
|
638
|
+
this.options = options || {};
|
|
639
|
+
this._initCanvasAndRenderer(canvasId, options);
|
|
640
|
+
},
|
|
641
|
+
|
|
642
|
+
updateOptions(options) {
|
|
643
|
+
this.options = options || {};
|
|
644
|
+
if (this.renderer) {
|
|
645
|
+
this.renderer.updateOptions(this.options);
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
|
|
649
|
+
setValue(v) {
|
|
650
|
+
if (this.renderer) {
|
|
651
|
+
this.renderer.setValue(v);
|
|
652
|
+
}
|
|
653
|
+
},
|
|
654
|
+
|
|
655
|
+
async _initCanvasAndRenderer(canvasId, options) {
|
|
656
|
+
dlog('init 开始 canvasId=' + canvasId);
|
|
657
|
+
var canvas = await getCanvasById(canvasId);
|
|
658
|
+
dlog('getCanvasById 返回', canvas ? 'ok' : 'null/undefined');
|
|
659
|
+
if (!canvas) {
|
|
660
|
+
dlog('拿不到 canvas,渲染与手势都不会生效;检查 canvasId 是否与 tyml 上的 canvas-id 一致');
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
this.canvasDom = canvas;
|
|
664
|
+
|
|
665
|
+
this.renderer = new ZoomTurntableRenderer({
|
|
666
|
+
canvas: canvas,
|
|
667
|
+
scale: this.options.scale,
|
|
668
|
+
orientation: this.options.orientation,
|
|
669
|
+
cropRatio: this.options.cropRatio,
|
|
670
|
+
min: this.options.min,
|
|
671
|
+
max: this.options.max,
|
|
672
|
+
step: this.options.step,
|
|
673
|
+
defaultValue: this.options.defaultValue,
|
|
674
|
+
marks: this.options.marks,
|
|
675
|
+
ticks: this.options.ticks,
|
|
676
|
+
curve: this.options.curve,
|
|
677
|
+
activeColor: this.options.activeColor,
|
|
678
|
+
styleOptions: this.options.styleOptions,
|
|
679
|
+
callMethod: this.callMethod.bind(this),
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
this.renderer.setupEventListeners();
|
|
683
|
+
},
|
|
684
|
+
|
|
685
|
+
destroy() {
|
|
686
|
+
if (this.renderer) {
|
|
687
|
+
this.renderer.destroy();
|
|
688
|
+
this.renderer = null;
|
|
689
|
+
}
|
|
690
|
+
},
|
|
691
|
+
});
|