@hxa-rn/react-native-gcanvas 6.0.24-beta.1 → 6.0.26
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/CHANGELOG.md +2 -2
- package/LICENSE +203 -21
- package/README.md +207 -207
- package/harmony/gcanvas/LICENSE +203 -21
- package/harmony/gcanvas/NOTICE +32 -32
- package/harmony/gcanvas/OAT.xml +37 -37
- package/harmony/gcanvas/build-profile.json5 +16 -16
- package/harmony/gcanvas/hvigorfile.ts +1 -1
- package/harmony/gcanvas/index.ets +1 -1
- package/harmony/gcanvas/oh-package.json5 +14 -14
- package/harmony/gcanvas/src/main/cpp/CMakeLists.txt +126 -126
- package/harmony/gcanvas/src/main/cpp/GCanvasNapi.cpp +485 -485
- package/harmony/gcanvas/src/main/cpp/GCanvasXComponent.cpp +106 -106
- package/harmony/gcanvas/src/main/cpp/GcanvasPackage.h +24 -24
- package/harmony/gcanvas/src/main/ets/GCanvasModuleTurboModule.ts +448 -448
- package/harmony/gcanvas/src/main/ets/GCanvasNativeEngine.ts +206 -206
- package/harmony/gcanvas/src/main/ets/components/GCanvasView.ets +64 -64
- package/harmony/gcanvas/src/main/module.json5 +21 -21
- package/harmony/gcanvas/src/main/resources/base/element/string.json +12 -12
- package/harmony/gcanvas/src/main/resources/en_US/element/string.json +8 -8
- package/harmony/gcanvas/src/main/resources/zh_CN/element/string.json +12 -12
- package/harmony/gcanvas.har +0 -0
- package/package.json +4 -4
- package/packages/gcanvas/.babelrc +4 -4
- package/packages/gcanvas/.eslintrc.js +133 -133
- package/packages/gcanvas/package.json +19 -19
- package/packages/gcanvas/rollup.config.js +13 -13
- package/packages/gcanvas/src/bridge/react-native.js +286 -286
- package/packages/gcanvas/src/bridge/weex.js +210 -210
- package/packages/gcanvas/src/context/2d/FillStyleLinearGradient.js +16 -16
- package/packages/gcanvas/src/context/2d/FillStylePattern.js +7 -7
- package/packages/gcanvas/src/context/2d/FillStyleRadialGradient.js +16 -16
- package/packages/gcanvas/src/context/2d/OffScreenRenderingContext.js +48 -48
- package/packages/gcanvas/src/context/2d/RenderingContext.js +787 -787
- package/packages/gcanvas/src/context/webgl/ActiveInfo.js +10 -10
- package/packages/gcanvas/src/context/webgl/Buffer.js +20 -20
- package/packages/gcanvas/src/context/webgl/Framebuffer.js +20 -20
- package/packages/gcanvas/src/context/webgl/GLenum.js +300 -300
- package/packages/gcanvas/src/context/webgl/GLmethod.js +141 -141
- package/packages/gcanvas/src/context/webgl/GLtype.js +22 -22
- package/packages/gcanvas/src/context/webgl/Program.js +20 -20
- package/packages/gcanvas/src/context/webgl/Renderbuffer.js +20 -20
- package/packages/gcanvas/src/context/webgl/RenderingContext.js +1479 -1479
- package/packages/gcanvas/src/context/webgl/Shader.js +21 -21
- package/packages/gcanvas/src/context/webgl/ShaderPrecisionFormat.js +10 -10
- package/packages/gcanvas/src/context/webgl/Texture.js +21 -21
- package/packages/gcanvas/src/context/webgl/UniformLocation.js +21 -21
- package/packages/gcanvas/src/context/webgl/classUtils.js +2 -2
- package/packages/gcanvas/src/env/canvas.js +210 -210
- package/packages/gcanvas/src/env/image.js +76 -76
- package/packages/gcanvas/src/env/off-screen-canvas.js +74 -74
- package/packages/gcanvas/src/index.js +56 -56
- package/src/components/CanvasView.js +20 -20
- package/src/components/GCanvasComponent.js +408 -408
- package/src/index.js +69 -69
- package/src/packages/gcanvas/.babelrc +4 -4
- package/src/packages/gcanvas/.eslintrc.js +133 -133
- package/src/packages/gcanvas/package.json +19 -19
- package/src/packages/gcanvas/rollup.config.js +13 -13
- package/src/packages/gcanvas/src/bridge/react-native.js +288 -288
- package/src/packages/gcanvas/src/bridge/weex.js +210 -210
- package/src/packages/gcanvas/src/context/2d/FillStyleLinearGradient.js +16 -16
- package/src/packages/gcanvas/src/context/2d/FillStylePattern.js +7 -7
- package/src/packages/gcanvas/src/context/2d/FillStyleRadialGradient.js +16 -16
- package/src/packages/gcanvas/src/context/2d/OffScreenRenderingContext.js +48 -48
- package/src/packages/gcanvas/src/context/2d/RenderingContext.js +801 -801
- package/src/packages/gcanvas/src/context/webgl/ActiveInfo.js +10 -10
- package/src/packages/gcanvas/src/context/webgl/Buffer.js +20 -20
- package/src/packages/gcanvas/src/context/webgl/Framebuffer.js +20 -20
- package/src/packages/gcanvas/src/context/webgl/GLenum.js +300 -300
- package/src/packages/gcanvas/src/context/webgl/GLmethod.js +141 -141
- package/src/packages/gcanvas/src/context/webgl/GLtype.js +22 -22
- package/src/packages/gcanvas/src/context/webgl/Program.js +20 -20
- package/src/packages/gcanvas/src/context/webgl/Renderbuffer.js +20 -20
- package/src/packages/gcanvas/src/context/webgl/RenderingContext.js +1479 -1479
- package/src/packages/gcanvas/src/context/webgl/Shader.js +21 -21
- package/src/packages/gcanvas/src/context/webgl/ShaderPrecisionFormat.js +10 -10
- package/src/packages/gcanvas/src/context/webgl/Texture.js +21 -21
- package/src/packages/gcanvas/src/context/webgl/UniformLocation.js +21 -21
- package/src/packages/gcanvas/src/context/webgl/classUtils.js +2 -2
- package/src/packages/gcanvas/src/env/canvas.js +210 -210
- package/src/packages/gcanvas/src/env/image.js +76 -76
- package/src/packages/gcanvas/src/env/off-screen-canvas.js +74 -74
- package/src/packages/gcanvas/src/index.js +56 -56
- package/src/specs/v1/GCanvasViewNativeComponent.ts +42 -42
- package/src/specs/v1/NativeGCanvasModule.ts +140 -140
|
@@ -1,408 +1,408 @@
|
|
|
1
|
-
import React, {Component} from 'react';
|
|
2
|
-
// import PropTypes from 'prop-types';
|
|
3
|
-
import {
|
|
4
|
-
NativeEventEmitter,
|
|
5
|
-
NativeModules,
|
|
6
|
-
PanResponder,
|
|
7
|
-
PixelRatio,
|
|
8
|
-
Platform,
|
|
9
|
-
Text,
|
|
10
|
-
View,
|
|
11
|
-
findNodeHandle,
|
|
12
|
-
} from 'react-native';
|
|
13
|
-
import '@flyskywhy/react-native-browser-polyfill';
|
|
14
|
-
import CanvasView from './CanvasView';
|
|
15
|
-
import {enable, disable, ReactNativeBridge} from '../packages/gcanvas';
|
|
16
|
-
import GCanvasModule from '../specs/v1/NativeGCanvasModule';
|
|
17
|
-
ReactNativeBridge.GCanvasModule = GCanvasModule;
|
|
18
|
-
ReactNativeBridge.Platform = Platform;
|
|
19
|
-
|
|
20
|
-
export default class GCanvasView extends Component {
|
|
21
|
-
constructor(props) {
|
|
22
|
-
super(props);
|
|
23
|
-
this.refCanvasView = null;
|
|
24
|
-
this.canvas = null;
|
|
25
|
-
this.panScale = 1;
|
|
26
|
-
this.touchBank = [];
|
|
27
|
-
this.canvasViewPageX = 0;
|
|
28
|
-
this.canvasViewPageY = 0;
|
|
29
|
-
this.pressedIds = new Set();
|
|
30
|
-
|
|
31
|
-
let panResponder = PanResponder.create({
|
|
32
|
-
onStartShouldSetPanResponder: () => true,
|
|
33
|
-
onMoveShouldSetPanResponder: () => false,
|
|
34
|
-
onPanResponderGrant: (event) => {
|
|
35
|
-
// as [PanResponder is not working when there are multiple touches](https://github.com/facebook/react-native/issues/8094)
|
|
36
|
-
// said, maybe it's a bug of react-native, and found (tested on Android) 2nd finger touch will
|
|
37
|
-
// not invoke onPanResponderGrant(), so be the work around this.touchBank
|
|
38
|
-
|
|
39
|
-
// even componentWillUnmount() inovked then componentDidMount() again, the
|
|
40
|
-
// event.touchHistory.touchBank still maintain before componentWillUnmount(),
|
|
41
|
-
// maybe it's a bug of react-native, so be the workaround here
|
|
42
|
-
this.touchBank = event.touchHistory.touchBank.filter(touch => !!touch).map(touch => {return {...touch, touchActive: false}});
|
|
43
|
-
// 原逻辑只在 Move 里补 mousedown;轻点不滑动就没有按下。Grant 时先派发。
|
|
44
|
-
this.nativeEventToTouches(event).forEach((touch) => this.emitPointer('mousedown', touch));
|
|
45
|
-
},
|
|
46
|
-
onPanResponderMove: (event, gestureState) => {
|
|
47
|
-
// tested multiple touches with babylonjs demo `Drag and drop` in https://github.com/flyskywhy/GCanvasRNExamples
|
|
48
|
-
let touchBank = event.touchHistory.touchBank.filter(touch => !!touch);
|
|
49
|
-
|
|
50
|
-
let toDownTouchs = touchBank.filter(cur => {
|
|
51
|
-
let preFound = this.touchBank.find(pre => this.touch2PointerId(pre) === this.touch2PointerId(cur));
|
|
52
|
-
if (preFound) {
|
|
53
|
-
if (!preFound.touchActive && cur.touchActive) {
|
|
54
|
-
// new finger touch can be here because `touchActive: false` workaround in onPanResponderGrant()
|
|
55
|
-
return true;
|
|
56
|
-
} else {
|
|
57
|
-
// componentWillUnmount() then componentDidMount() again cause
|
|
58
|
-
// event.touchHistory.touchBank still maintain before componentWillUnmount() will
|
|
59
|
-
// preFound.touchActive === false and cur.touchActive === false
|
|
60
|
-
// to be here
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
} else {
|
|
64
|
-
// 2nd finger touch will be here as 2nd finger touch will not invoke onPanResponderGrant() to change this.touchBank
|
|
65
|
-
return true;
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
let toUpTouchs = this.touchBank.filter(pre => touchBank.filter(cur => this.touch2PointerId(cur) === this.touch2PointerId(pre) && !cur.touchActive).length && pre.touchActive);
|
|
69
|
-
let toMoveTouchs = touchBank.filter(cur => {
|
|
70
|
-
if (cur.touchActive) {
|
|
71
|
-
if (toDownTouchs.filter(down => this.touch2PointerId(down) === this.touch2PointerId(cur)).length) {
|
|
72
|
-
// let 'down' -> 'up' that without 'move' in middle can be possible, thus present same behavior with Web
|
|
73
|
-
return false;
|
|
74
|
-
} else {
|
|
75
|
-
return true;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// need ...touch here otherwise this.touchBank will change automatically with touchBank
|
|
81
|
-
this.touchBank = touchBank.map(touch => {return {...touch}});
|
|
82
|
-
|
|
83
|
-
toDownTouchs.map(touch => this.emitPointer('mousedown', touch));
|
|
84
|
-
toUpTouchs.map(touch => this.emitPointer('mouseup', touch));
|
|
85
|
-
toMoveTouchs.map(touch => this.emitPointer('mousemove', touch));
|
|
86
|
-
},
|
|
87
|
-
onPanResponderRelease: (event, gestureState) => {
|
|
88
|
-
// found 2nd finger touch release will not invoke onPanResponderRelease(), so be the work around this.touchBank with toUpTouchs above
|
|
89
|
-
this.touchBank.map(touch => {
|
|
90
|
-
if (touch.touchActive) {
|
|
91
|
-
// actually only last finger touch release will touch.touchActive === true
|
|
92
|
-
this.emitPointer('mouseup', touch);
|
|
93
|
-
touch.touchActive = false;
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
this.nativeEventToTouches(event).forEach((touch) => this.emitPointer('mouseup', touch));
|
|
97
|
-
},
|
|
98
|
-
onPanResponderTerminationRequest: () => false,
|
|
99
|
-
onPanResponderTerminate: () => false,
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
this.state = {
|
|
103
|
-
panResponder: {},
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
if (props.isGestureResponsible) {
|
|
107
|
-
this.state.panResponder = panResponder;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
static propTypes = {
|
|
112
|
-
// isOffscreen: PropTypes.bool,
|
|
113
|
-
// NOTE: View.propTypes 已在 React 16+ / RN 0.71+ 移除(含 ViewPropTypes),
|
|
114
|
-
// 在鸿蒙 RN 基座加载即抛 TypeError 白屏,故仅保留自定义 prop 类型注释。
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
static defaultProps = {
|
|
118
|
-
// Indicate whether response for gesture inside canvas,
|
|
119
|
-
// so that PanResponder outside GCanvasView can be useable
|
|
120
|
-
// when isGestureResponsible is false.
|
|
121
|
-
// Default is true, so that zdog can be "mousemove".
|
|
122
|
-
isGestureResponsible: true,
|
|
123
|
-
// only affect 2d
|
|
124
|
-
// on Web, putImageData looks like will auto clearRect before,
|
|
125
|
-
// but I think it's more convenient to not clearRect before
|
|
126
|
-
// putImageData when imageData has some transparent pixels,
|
|
127
|
-
// so the default value is false, and if you want to be exactly
|
|
128
|
-
// compatible with Web, you can set it to true
|
|
129
|
-
isAutoClearRectBeforePutImageData: false,
|
|
130
|
-
// With canvas 2d, generally be true, so that can resolve issue
|
|
131
|
-
// [Canvas resize](https://github.com/flyskywhy/react-native-gcanvas/issues/32).
|
|
132
|
-
// If canvas 2d also have ctx call loop like usage of 'zdog', also can be true.
|
|
133
|
-
// With webgl 3d, generally be false, so that can
|
|
134
|
-
// [let webgl 3d can continue gl call loop without restart it by drawSome() after rotate screenOrientation]
|
|
135
|
-
// (https://github.com/flyskywhy/GCanvasRNExamples/commit/3cd2f3f).
|
|
136
|
-
isResetGlViewportAfterSetWidthOrHeight: true,
|
|
137
|
-
// devicePixelRatio default is undefined and means default is PixelRatio.get() ,
|
|
138
|
-
// ref to "About devicePixelRatio" in README.md
|
|
139
|
-
devicePixelRatio: undefined,
|
|
140
|
-
// Indicate whether this canvas can be use by document.createElement('canvas') (as offscreen canvas),
|
|
141
|
-
// ref to README.md for more usage attentions like `zIndex: -100` in style
|
|
142
|
-
offscreenCanvas: false,
|
|
143
|
-
// only affect webgl
|
|
144
|
-
// false: use AutoSwap, means gcanvas use a setInterval(render, 16) to exec cached cmds
|
|
145
|
-
// to generate and display graphics
|
|
146
|
-
// true: not use AutoSwap, means APP use it's own loop to call gl.clear(), thus gcanvas
|
|
147
|
-
// will exec cached cmds to generate and display graphics, then add gl.clear to
|
|
148
|
-
// cmds cache to be exec next time, and also offer APP a canvas._swapBuffers() if
|
|
149
|
-
// APP want exec cached cmds to generate and display graphics manually
|
|
150
|
-
disableAutoSwap: false,
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
nativeEventToTouches = (evt) => {
|
|
154
|
-
const ne = (evt && evt.nativeEvent) || {};
|
|
155
|
-
const list = (ne.changedTouches && ne.changedTouches.length)
|
|
156
|
-
? ne.changedTouches
|
|
157
|
-
: (ne.touches && ne.touches.length)
|
|
158
|
-
? ne.touches
|
|
159
|
-
: [ne];
|
|
160
|
-
return list.filter(Boolean).map((t, i) => {
|
|
161
|
-
const locationX = t.locationX ?? ne.locationX;
|
|
162
|
-
const locationY = t.locationY ?? ne.locationY;
|
|
163
|
-
const pageX = t.pageX ?? ne.pageX ??
|
|
164
|
-
(locationX != null ? locationX + this.canvasViewPageX : 0);
|
|
165
|
-
const pageY = t.pageY ?? ne.pageY ??
|
|
166
|
-
(locationY != null ? locationY + this.canvasViewPageY : 0);
|
|
167
|
-
return {
|
|
168
|
-
currentPageX: pageX,
|
|
169
|
-
currentPageY: pageY,
|
|
170
|
-
startPageX: t.startPageX ?? pageX,
|
|
171
|
-
startPageY: t.startPageY ?? pageY,
|
|
172
|
-
startTimeStamp: t.identifier ?? t.startTimeStamp ?? i,
|
|
173
|
-
currentTimeStamp: t.timestamp ?? ne.timestamp ?? Date.now(),
|
|
174
|
-
touchActive: true,
|
|
175
|
-
};
|
|
176
|
-
});
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
emitPointer = (type, touch) => {
|
|
180
|
-
const id = this.touch2PointerId(touch);
|
|
181
|
-
if (type === 'mousedown') {
|
|
182
|
-
if (this.pressedIds.has(id)) {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
this.pressedIds.add(id);
|
|
186
|
-
} else if (type === 'mouseup') {
|
|
187
|
-
if (!this.pressedIds.has(id) && this.pressedIds.size === 0) {
|
|
188
|
-
// 仍派发抬起:鸿蒙上 Grant/Start 可能丢了,用户至少能看到 mouseup
|
|
189
|
-
}
|
|
190
|
-
this.pressedIds.delete(id);
|
|
191
|
-
}
|
|
192
|
-
this.dispatch({touch, type});
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
onHarmonyTouchStart = (evt) => {
|
|
196
|
-
this.nativeEventToTouches(evt).forEach((touch) => this.emitPointer('mousedown', touch));
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
onHarmonyTouchMove = (evt) => {
|
|
200
|
-
this.nativeEventToTouches(evt).forEach((touch) => this.emitPointer('mousemove', touch));
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
onHarmonyTouchEnd = (evt) => {
|
|
204
|
-
this.nativeEventToTouches(evt).forEach((touch) => this.emitPointer('mouseup', touch));
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
dispatch = ({touch, type}) => {
|
|
208
|
-
let pointerEvent = this.touch2PointerEvent({touch, type});
|
|
209
|
-
if (this.canvas) {
|
|
210
|
-
this.canvas.dispatchEvent(pointerEvent);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// as `node_modules/zdog/js/dragger.js` use window.addEventListener not element.addEventListener on mousemove
|
|
214
|
-
window.dispatchEvent(pointerEvent);
|
|
215
|
-
|
|
216
|
-
switch (type) {
|
|
217
|
-
case 'mousemove':
|
|
218
|
-
this.props.onMouseMove && this.props.onMouseMove(pointerEvent);
|
|
219
|
-
break;
|
|
220
|
-
case 'mousedown':
|
|
221
|
-
this.props.onMouseDown && this.props.onMouseDown(pointerEvent);
|
|
222
|
-
break;
|
|
223
|
-
case 'mouseup':
|
|
224
|
-
this.props.onMouseUp && this.props.onMouseUp(pointerEvent);
|
|
225
|
-
break;
|
|
226
|
-
default:
|
|
227
|
-
break;
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
touch2PointerEvent = ({touch, type}) => {
|
|
232
|
-
return {
|
|
233
|
-
altKey: false,
|
|
234
|
-
button: type === 'mousemove' ? -1 : 0,
|
|
235
|
-
buttons: type === 'mousemove' ? 0 : 1,
|
|
236
|
-
clientX: (touch.currentPageX - this.canvasViewPageX) * this.panScale,
|
|
237
|
-
clientY: (touch.currentPageY - this.canvasViewPageY) * this.panScale,
|
|
238
|
-
ctrlKey: false,
|
|
239
|
-
isTrusted: true,
|
|
240
|
-
metaKey: false,
|
|
241
|
-
pageX: touch.currentPageX * this.panScale,
|
|
242
|
-
pageY: touch.currentPageY * this.panScale,
|
|
243
|
-
pointerId: this.touch2PointerId(touch),
|
|
244
|
-
pointerType: "touch",
|
|
245
|
-
shiftKey: false,
|
|
246
|
-
target: this.canvas,
|
|
247
|
-
timeStamp: touch.currentTimeStamp,
|
|
248
|
-
type, // to works with babylonjs, type should not be 'pointerdown', 'pointermove' or 'pointerup'
|
|
249
|
-
};
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
touch2PointerId = (touch) => touch.startTimeStamp + touch.startPageX + touch.startPageY;
|
|
253
|
-
|
|
254
|
-
_onIsReady = (event) => {
|
|
255
|
-
if (this.props.onIsReady) {
|
|
256
|
-
this.props.onIsReady(
|
|
257
|
-
Platform.OS === 'ios' ? true : event.nativeEvent.value,
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
_onLayout = (event) => {
|
|
263
|
-
let width = event.nativeEvent.layout.width | 0; // width is fixed not float just like Web
|
|
264
|
-
let height = event.nativeEvent.layout.height | 0;
|
|
265
|
-
let ref = '' + findNodeHandle(this.refCanvasView);
|
|
266
|
-
|
|
267
|
-
this.refCanvasView && this.refCanvasView.measure((x, y, width, height, pageX, pageY) => {
|
|
268
|
-
this.canvasViewPageX = pageX;
|
|
269
|
-
this.canvasViewPageY = pageY;
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
// When onLayout is invoked again (e.g. change phone orientation), if assign
|
|
273
|
-
// `this.canvas` again, that also means `this` in dispatchEvent() of
|
|
274
|
-
// `event-target-shim/dist/event-target-shim.js` changed, thus dispatchEvent()
|
|
275
|
-
// can do nothing and cause `node_modules/zdog/js/dragger.js` can't be moved
|
|
276
|
-
// by finger anymore.
|
|
277
|
-
// So let `this.canvas` be assigned here only once.
|
|
278
|
-
if (this.canvas !== null) {
|
|
279
|
-
if (this.canvas.clientWidth !== width || this.canvas.clientHeight !== height) {
|
|
280
|
-
this.canvas.clientWidth = width;
|
|
281
|
-
this.canvas.clientHeight = height;
|
|
282
|
-
if (this.props.onCanvasResize) {
|
|
283
|
-
// APP can `this.canvas.width = width` in onCanvasResize()
|
|
284
|
-
this.props.onCanvasResize({width, height, canvas: this.canvas});
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (this.refCanvasView === null) {
|
|
291
|
-
this._onLayout(event);
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
this.canvas = enable(
|
|
296
|
-
{
|
|
297
|
-
ref,
|
|
298
|
-
style: {
|
|
299
|
-
width,
|
|
300
|
-
height,
|
|
301
|
-
},
|
|
302
|
-
},
|
|
303
|
-
{
|
|
304
|
-
isAutoClearRectBeforePutImageData: this.props.isAutoClearRectBeforePutImageData,
|
|
305
|
-
isResetGlViewportAfterSetWidthOrHeight: this.props.isResetGlViewportAfterSetWidthOrHeight,
|
|
306
|
-
devicePixelRatio: this.props.devicePixelRatio,
|
|
307
|
-
disableAutoSwap: this.props.disableAutoSwap,
|
|
308
|
-
bridge: ReactNativeBridge,
|
|
309
|
-
},
|
|
310
|
-
);
|
|
311
|
-
|
|
312
|
-
this.panScale = PixelRatio.get() / this.canvas._devicePixelRatio;
|
|
313
|
-
|
|
314
|
-
if (this.props.offscreenCanvas && !global.createCanvasElements.includes(this.canvas)) {
|
|
315
|
-
global.createCanvasElements.push(this.canvas);
|
|
316
|
-
if (global.createCanvasElementsObj) {
|
|
317
|
-
// ios release (RN0.71.6 JSC) createCanvasElements.push(canvas) in a class but still
|
|
318
|
-
// get [] means createCanvasElements.length is 0 in another class, so have to
|
|
319
|
-
// use createCanvasElementsObj below, and reserve createCanvasElements for compatible
|
|
320
|
-
global.createCanvasElementsObj[this.canvas.id] = this.canvas;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
if (this.props.onCanvasCreate) {
|
|
325
|
-
this.props.onCanvasCreate(this.canvas);
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
componentDidMount() {
|
|
330
|
-
// on iOS, sometimes setLogLevel(0) will cause APP stuck if running in Xcode (because too many logs?), but setLogLevel(0)
|
|
331
|
-
// will not cause APP stuck if not running in damn Xcode, tested in https://github.com/flyskywhy/snakeRN
|
|
332
|
-
// ReactNativeBridge.GCanvasModule.setLogLevel(0); // 0 means DEBUG
|
|
333
|
-
|
|
334
|
-
// since https://github.com/flyskywhy/react-native-gcanvas/issues/44 said latest RN
|
|
335
|
-
// will warning `EventEmitter.removeListener ... Method has been deprecated` and
|
|
336
|
-
// only iOS use EventEmitter and on iOS always true in _onIsReady(), so just comment below
|
|
337
|
-
// if (Platform.OS === 'ios') {
|
|
338
|
-
// // while always true in _onIsReady(), here is just to suppress warning
|
|
339
|
-
// // on iOS Sending `GCanvasReady` with no listeners registered.
|
|
340
|
-
// const emitter = new NativeEventEmitter(ReactNativeBridge.GCanvasModule);
|
|
341
|
-
// emitter.addListener('GCanvasReady', this._onIsReady);
|
|
342
|
-
// }
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
componentWillUnmount() {
|
|
346
|
-
if (this.canvas !== null) {
|
|
347
|
-
let index = global.createCanvasElements.findIndex(canvas => canvas === this.canvas);
|
|
348
|
-
if (index >= 0) {
|
|
349
|
-
global.createCanvasElements.splice(index, 1);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
if (global.createCanvasElementsObj && global.createCanvasElementsObj[this.canvas.id]) {
|
|
353
|
-
delete global.createCanvasElementsObj[this.canvas.id];
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
disable(this.canvas);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// if (Platform.OS === 'ios') {
|
|
360
|
-
// const emitter = new NativeEventEmitter(ReactNativeBridge.GCanvasModule);
|
|
361
|
-
// emitter.removeListener('GCanvasReady', this._onIsReady);
|
|
362
|
-
// }
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
render() {
|
|
366
|
-
if (Platform.OS === 'web') {
|
|
367
|
-
return (
|
|
368
|
-
<View {...this.props}>
|
|
369
|
-
<Text>{'Please use <canvas> not <CanvasView> on Web'}</Text>
|
|
370
|
-
</View>
|
|
371
|
-
);
|
|
372
|
-
} else if (Platform.OS === 'harmony' && this.props.isGestureResponsible) {
|
|
373
|
-
// XComponent SURFACE 常只把 move 交给 RN PanResponder,按下/抬起丢失。
|
|
374
|
-
// 外包 RN View 收 onTouch*;同时独占 responder,避免外层 ScrollView 竖滑抢走后触发 cancel→mouseup。
|
|
375
|
-
return (
|
|
376
|
-
<View
|
|
377
|
-
style={this.props.style}
|
|
378
|
-
collapsable={false}
|
|
379
|
-
onStartShouldSetResponder={() => true}
|
|
380
|
-
onMoveShouldSetResponder={() => true}
|
|
381
|
-
onResponderTerminationRequest={() => false}
|
|
382
|
-
onTouchStart={this.onHarmonyTouchStart}
|
|
383
|
-
onTouchMove={this.onHarmonyTouchMove}
|
|
384
|
-
onTouchEnd={this.onHarmonyTouchEnd}
|
|
385
|
-
onTouchCancel={this.onHarmonyTouchEnd}>
|
|
386
|
-
<CanvasView
|
|
387
|
-
{...this.props}
|
|
388
|
-
style={{width: '100%', height: '100%'}}
|
|
389
|
-
pointerEvents="none"
|
|
390
|
-
ref={(view) => (this.refCanvasView = view)}
|
|
391
|
-
onLayout={this._onLayout}
|
|
392
|
-
onChange={this._onIsReady}
|
|
393
|
-
/>
|
|
394
|
-
</View>
|
|
395
|
-
);
|
|
396
|
-
} else {
|
|
397
|
-
return (
|
|
398
|
-
<CanvasView
|
|
399
|
-
{...this.props}
|
|
400
|
-
{...this.state.panResponder.panHandlers}
|
|
401
|
-
ref={(view) => (this.refCanvasView = view)}
|
|
402
|
-
onLayout={this._onLayout}
|
|
403
|
-
onChange={this._onIsReady}
|
|
404
|
-
/>
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
1
|
+
import React, {Component} from 'react';
|
|
2
|
+
// import PropTypes from 'prop-types';
|
|
3
|
+
import {
|
|
4
|
+
NativeEventEmitter,
|
|
5
|
+
NativeModules,
|
|
6
|
+
PanResponder,
|
|
7
|
+
PixelRatio,
|
|
8
|
+
Platform,
|
|
9
|
+
Text,
|
|
10
|
+
View,
|
|
11
|
+
findNodeHandle,
|
|
12
|
+
} from 'react-native';
|
|
13
|
+
import '@flyskywhy/react-native-browser-polyfill';
|
|
14
|
+
import CanvasView from './CanvasView';
|
|
15
|
+
import {enable, disable, ReactNativeBridge} from '../packages/gcanvas';
|
|
16
|
+
import GCanvasModule from '../specs/v1/NativeGCanvasModule';
|
|
17
|
+
ReactNativeBridge.GCanvasModule = GCanvasModule;
|
|
18
|
+
ReactNativeBridge.Platform = Platform;
|
|
19
|
+
|
|
20
|
+
export default class GCanvasView extends Component {
|
|
21
|
+
constructor(props) {
|
|
22
|
+
super(props);
|
|
23
|
+
this.refCanvasView = null;
|
|
24
|
+
this.canvas = null;
|
|
25
|
+
this.panScale = 1;
|
|
26
|
+
this.touchBank = [];
|
|
27
|
+
this.canvasViewPageX = 0;
|
|
28
|
+
this.canvasViewPageY = 0;
|
|
29
|
+
this.pressedIds = new Set();
|
|
30
|
+
|
|
31
|
+
let panResponder = PanResponder.create({
|
|
32
|
+
onStartShouldSetPanResponder: () => true,
|
|
33
|
+
onMoveShouldSetPanResponder: () => false,
|
|
34
|
+
onPanResponderGrant: (event) => {
|
|
35
|
+
// as [PanResponder is not working when there are multiple touches](https://github.com/facebook/react-native/issues/8094)
|
|
36
|
+
// said, maybe it's a bug of react-native, and found (tested on Android) 2nd finger touch will
|
|
37
|
+
// not invoke onPanResponderGrant(), so be the work around this.touchBank
|
|
38
|
+
|
|
39
|
+
// even componentWillUnmount() inovked then componentDidMount() again, the
|
|
40
|
+
// event.touchHistory.touchBank still maintain before componentWillUnmount(),
|
|
41
|
+
// maybe it's a bug of react-native, so be the workaround here
|
|
42
|
+
this.touchBank = event.touchHistory.touchBank.filter(touch => !!touch).map(touch => {return {...touch, touchActive: false}});
|
|
43
|
+
// 原逻辑只在 Move 里补 mousedown;轻点不滑动就没有按下。Grant 时先派发。
|
|
44
|
+
this.nativeEventToTouches(event).forEach((touch) => this.emitPointer('mousedown', touch));
|
|
45
|
+
},
|
|
46
|
+
onPanResponderMove: (event, gestureState) => {
|
|
47
|
+
// tested multiple touches with babylonjs demo `Drag and drop` in https://github.com/flyskywhy/GCanvasRNExamples
|
|
48
|
+
let touchBank = event.touchHistory.touchBank.filter(touch => !!touch);
|
|
49
|
+
|
|
50
|
+
let toDownTouchs = touchBank.filter(cur => {
|
|
51
|
+
let preFound = this.touchBank.find(pre => this.touch2PointerId(pre) === this.touch2PointerId(cur));
|
|
52
|
+
if (preFound) {
|
|
53
|
+
if (!preFound.touchActive && cur.touchActive) {
|
|
54
|
+
// new finger touch can be here because `touchActive: false` workaround in onPanResponderGrant()
|
|
55
|
+
return true;
|
|
56
|
+
} else {
|
|
57
|
+
// componentWillUnmount() then componentDidMount() again cause
|
|
58
|
+
// event.touchHistory.touchBank still maintain before componentWillUnmount() will
|
|
59
|
+
// preFound.touchActive === false and cur.touchActive === false
|
|
60
|
+
// to be here
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
// 2nd finger touch will be here as 2nd finger touch will not invoke onPanResponderGrant() to change this.touchBank
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
let toUpTouchs = this.touchBank.filter(pre => touchBank.filter(cur => this.touch2PointerId(cur) === this.touch2PointerId(pre) && !cur.touchActive).length && pre.touchActive);
|
|
69
|
+
let toMoveTouchs = touchBank.filter(cur => {
|
|
70
|
+
if (cur.touchActive) {
|
|
71
|
+
if (toDownTouchs.filter(down => this.touch2PointerId(down) === this.touch2PointerId(cur)).length) {
|
|
72
|
+
// let 'down' -> 'up' that without 'move' in middle can be possible, thus present same behavior with Web
|
|
73
|
+
return false;
|
|
74
|
+
} else {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// need ...touch here otherwise this.touchBank will change automatically with touchBank
|
|
81
|
+
this.touchBank = touchBank.map(touch => {return {...touch}});
|
|
82
|
+
|
|
83
|
+
toDownTouchs.map(touch => this.emitPointer('mousedown', touch));
|
|
84
|
+
toUpTouchs.map(touch => this.emitPointer('mouseup', touch));
|
|
85
|
+
toMoveTouchs.map(touch => this.emitPointer('mousemove', touch));
|
|
86
|
+
},
|
|
87
|
+
onPanResponderRelease: (event, gestureState) => {
|
|
88
|
+
// found 2nd finger touch release will not invoke onPanResponderRelease(), so be the work around this.touchBank with toUpTouchs above
|
|
89
|
+
this.touchBank.map(touch => {
|
|
90
|
+
if (touch.touchActive) {
|
|
91
|
+
// actually only last finger touch release will touch.touchActive === true
|
|
92
|
+
this.emitPointer('mouseup', touch);
|
|
93
|
+
touch.touchActive = false;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
this.nativeEventToTouches(event).forEach((touch) => this.emitPointer('mouseup', touch));
|
|
97
|
+
},
|
|
98
|
+
onPanResponderTerminationRequest: () => false,
|
|
99
|
+
onPanResponderTerminate: () => false,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
this.state = {
|
|
103
|
+
panResponder: {},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
if (props.isGestureResponsible) {
|
|
107
|
+
this.state.panResponder = panResponder;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static propTypes = {
|
|
112
|
+
// isOffscreen: PropTypes.bool,
|
|
113
|
+
// NOTE: View.propTypes 已在 React 16+ / RN 0.71+ 移除(含 ViewPropTypes),
|
|
114
|
+
// 在鸿蒙 RN 基座加载即抛 TypeError 白屏,故仅保留自定义 prop 类型注释。
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
static defaultProps = {
|
|
118
|
+
// Indicate whether response for gesture inside canvas,
|
|
119
|
+
// so that PanResponder outside GCanvasView can be useable
|
|
120
|
+
// when isGestureResponsible is false.
|
|
121
|
+
// Default is true, so that zdog can be "mousemove".
|
|
122
|
+
isGestureResponsible: true,
|
|
123
|
+
// only affect 2d
|
|
124
|
+
// on Web, putImageData looks like will auto clearRect before,
|
|
125
|
+
// but I think it's more convenient to not clearRect before
|
|
126
|
+
// putImageData when imageData has some transparent pixels,
|
|
127
|
+
// so the default value is false, and if you want to be exactly
|
|
128
|
+
// compatible with Web, you can set it to true
|
|
129
|
+
isAutoClearRectBeforePutImageData: false,
|
|
130
|
+
// With canvas 2d, generally be true, so that can resolve issue
|
|
131
|
+
// [Canvas resize](https://github.com/flyskywhy/react-native-gcanvas/issues/32).
|
|
132
|
+
// If canvas 2d also have ctx call loop like usage of 'zdog', also can be true.
|
|
133
|
+
// With webgl 3d, generally be false, so that can
|
|
134
|
+
// [let webgl 3d can continue gl call loop without restart it by drawSome() after rotate screenOrientation]
|
|
135
|
+
// (https://github.com/flyskywhy/GCanvasRNExamples/commit/3cd2f3f).
|
|
136
|
+
isResetGlViewportAfterSetWidthOrHeight: true,
|
|
137
|
+
// devicePixelRatio default is undefined and means default is PixelRatio.get() ,
|
|
138
|
+
// ref to "About devicePixelRatio" in README.md
|
|
139
|
+
devicePixelRatio: undefined,
|
|
140
|
+
// Indicate whether this canvas can be use by document.createElement('canvas') (as offscreen canvas),
|
|
141
|
+
// ref to README.md for more usage attentions like `zIndex: -100` in style
|
|
142
|
+
offscreenCanvas: false,
|
|
143
|
+
// only affect webgl
|
|
144
|
+
// false: use AutoSwap, means gcanvas use a setInterval(render, 16) to exec cached cmds
|
|
145
|
+
// to generate and display graphics
|
|
146
|
+
// true: not use AutoSwap, means APP use it's own loop to call gl.clear(), thus gcanvas
|
|
147
|
+
// will exec cached cmds to generate and display graphics, then add gl.clear to
|
|
148
|
+
// cmds cache to be exec next time, and also offer APP a canvas._swapBuffers() if
|
|
149
|
+
// APP want exec cached cmds to generate and display graphics manually
|
|
150
|
+
disableAutoSwap: false,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
nativeEventToTouches = (evt) => {
|
|
154
|
+
const ne = (evt && evt.nativeEvent) || {};
|
|
155
|
+
const list = (ne.changedTouches && ne.changedTouches.length)
|
|
156
|
+
? ne.changedTouches
|
|
157
|
+
: (ne.touches && ne.touches.length)
|
|
158
|
+
? ne.touches
|
|
159
|
+
: [ne];
|
|
160
|
+
return list.filter(Boolean).map((t, i) => {
|
|
161
|
+
const locationX = t.locationX ?? ne.locationX;
|
|
162
|
+
const locationY = t.locationY ?? ne.locationY;
|
|
163
|
+
const pageX = t.pageX ?? ne.pageX ??
|
|
164
|
+
(locationX != null ? locationX + this.canvasViewPageX : 0);
|
|
165
|
+
const pageY = t.pageY ?? ne.pageY ??
|
|
166
|
+
(locationY != null ? locationY + this.canvasViewPageY : 0);
|
|
167
|
+
return {
|
|
168
|
+
currentPageX: pageX,
|
|
169
|
+
currentPageY: pageY,
|
|
170
|
+
startPageX: t.startPageX ?? pageX,
|
|
171
|
+
startPageY: t.startPageY ?? pageY,
|
|
172
|
+
startTimeStamp: t.identifier ?? t.startTimeStamp ?? i,
|
|
173
|
+
currentTimeStamp: t.timestamp ?? ne.timestamp ?? Date.now(),
|
|
174
|
+
touchActive: true,
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
emitPointer = (type, touch) => {
|
|
180
|
+
const id = this.touch2PointerId(touch);
|
|
181
|
+
if (type === 'mousedown') {
|
|
182
|
+
if (this.pressedIds.has(id)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
this.pressedIds.add(id);
|
|
186
|
+
} else if (type === 'mouseup') {
|
|
187
|
+
if (!this.pressedIds.has(id) && this.pressedIds.size === 0) {
|
|
188
|
+
// 仍派发抬起:鸿蒙上 Grant/Start 可能丢了,用户至少能看到 mouseup
|
|
189
|
+
}
|
|
190
|
+
this.pressedIds.delete(id);
|
|
191
|
+
}
|
|
192
|
+
this.dispatch({touch, type});
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
onHarmonyTouchStart = (evt) => {
|
|
196
|
+
this.nativeEventToTouches(evt).forEach((touch) => this.emitPointer('mousedown', touch));
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
onHarmonyTouchMove = (evt) => {
|
|
200
|
+
this.nativeEventToTouches(evt).forEach((touch) => this.emitPointer('mousemove', touch));
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
onHarmonyTouchEnd = (evt) => {
|
|
204
|
+
this.nativeEventToTouches(evt).forEach((touch) => this.emitPointer('mouseup', touch));
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
dispatch = ({touch, type}) => {
|
|
208
|
+
let pointerEvent = this.touch2PointerEvent({touch, type});
|
|
209
|
+
if (this.canvas) {
|
|
210
|
+
this.canvas.dispatchEvent(pointerEvent);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// as `node_modules/zdog/js/dragger.js` use window.addEventListener not element.addEventListener on mousemove
|
|
214
|
+
window.dispatchEvent(pointerEvent);
|
|
215
|
+
|
|
216
|
+
switch (type) {
|
|
217
|
+
case 'mousemove':
|
|
218
|
+
this.props.onMouseMove && this.props.onMouseMove(pointerEvent);
|
|
219
|
+
break;
|
|
220
|
+
case 'mousedown':
|
|
221
|
+
this.props.onMouseDown && this.props.onMouseDown(pointerEvent);
|
|
222
|
+
break;
|
|
223
|
+
case 'mouseup':
|
|
224
|
+
this.props.onMouseUp && this.props.onMouseUp(pointerEvent);
|
|
225
|
+
break;
|
|
226
|
+
default:
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
touch2PointerEvent = ({touch, type}) => {
|
|
232
|
+
return {
|
|
233
|
+
altKey: false,
|
|
234
|
+
button: type === 'mousemove' ? -1 : 0,
|
|
235
|
+
buttons: type === 'mousemove' ? 0 : 1,
|
|
236
|
+
clientX: (touch.currentPageX - this.canvasViewPageX) * this.panScale,
|
|
237
|
+
clientY: (touch.currentPageY - this.canvasViewPageY) * this.panScale,
|
|
238
|
+
ctrlKey: false,
|
|
239
|
+
isTrusted: true,
|
|
240
|
+
metaKey: false,
|
|
241
|
+
pageX: touch.currentPageX * this.panScale,
|
|
242
|
+
pageY: touch.currentPageY * this.panScale,
|
|
243
|
+
pointerId: this.touch2PointerId(touch),
|
|
244
|
+
pointerType: "touch",
|
|
245
|
+
shiftKey: false,
|
|
246
|
+
target: this.canvas,
|
|
247
|
+
timeStamp: touch.currentTimeStamp,
|
|
248
|
+
type, // to works with babylonjs, type should not be 'pointerdown', 'pointermove' or 'pointerup'
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
touch2PointerId = (touch) => touch.startTimeStamp + touch.startPageX + touch.startPageY;
|
|
253
|
+
|
|
254
|
+
_onIsReady = (event) => {
|
|
255
|
+
if (this.props.onIsReady) {
|
|
256
|
+
this.props.onIsReady(
|
|
257
|
+
Platform.OS === 'ios' ? true : event.nativeEvent.value,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
_onLayout = (event) => {
|
|
263
|
+
let width = event.nativeEvent.layout.width | 0; // width is fixed not float just like Web
|
|
264
|
+
let height = event.nativeEvent.layout.height | 0;
|
|
265
|
+
let ref = '' + findNodeHandle(this.refCanvasView);
|
|
266
|
+
|
|
267
|
+
this.refCanvasView && this.refCanvasView.measure((x, y, width, height, pageX, pageY) => {
|
|
268
|
+
this.canvasViewPageX = pageX;
|
|
269
|
+
this.canvasViewPageY = pageY;
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// When onLayout is invoked again (e.g. change phone orientation), if assign
|
|
273
|
+
// `this.canvas` again, that also means `this` in dispatchEvent() of
|
|
274
|
+
// `event-target-shim/dist/event-target-shim.js` changed, thus dispatchEvent()
|
|
275
|
+
// can do nothing and cause `node_modules/zdog/js/dragger.js` can't be moved
|
|
276
|
+
// by finger anymore.
|
|
277
|
+
// So let `this.canvas` be assigned here only once.
|
|
278
|
+
if (this.canvas !== null) {
|
|
279
|
+
if (this.canvas.clientWidth !== width || this.canvas.clientHeight !== height) {
|
|
280
|
+
this.canvas.clientWidth = width;
|
|
281
|
+
this.canvas.clientHeight = height;
|
|
282
|
+
if (this.props.onCanvasResize) {
|
|
283
|
+
// APP can `this.canvas.width = width` in onCanvasResize()
|
|
284
|
+
this.props.onCanvasResize({width, height, canvas: this.canvas});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (this.refCanvasView === null) {
|
|
291
|
+
this._onLayout(event);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
this.canvas = enable(
|
|
296
|
+
{
|
|
297
|
+
ref,
|
|
298
|
+
style: {
|
|
299
|
+
width,
|
|
300
|
+
height,
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
isAutoClearRectBeforePutImageData: this.props.isAutoClearRectBeforePutImageData,
|
|
305
|
+
isResetGlViewportAfterSetWidthOrHeight: this.props.isResetGlViewportAfterSetWidthOrHeight,
|
|
306
|
+
devicePixelRatio: this.props.devicePixelRatio,
|
|
307
|
+
disableAutoSwap: this.props.disableAutoSwap,
|
|
308
|
+
bridge: ReactNativeBridge,
|
|
309
|
+
},
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
this.panScale = PixelRatio.get() / this.canvas._devicePixelRatio;
|
|
313
|
+
|
|
314
|
+
if (this.props.offscreenCanvas && !global.createCanvasElements.includes(this.canvas)) {
|
|
315
|
+
global.createCanvasElements.push(this.canvas);
|
|
316
|
+
if (global.createCanvasElementsObj) {
|
|
317
|
+
// ios release (RN0.71.6 JSC) createCanvasElements.push(canvas) in a class but still
|
|
318
|
+
// get [] means createCanvasElements.length is 0 in another class, so have to
|
|
319
|
+
// use createCanvasElementsObj below, and reserve createCanvasElements for compatible
|
|
320
|
+
global.createCanvasElementsObj[this.canvas.id] = this.canvas;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (this.props.onCanvasCreate) {
|
|
325
|
+
this.props.onCanvasCreate(this.canvas);
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
componentDidMount() {
|
|
330
|
+
// on iOS, sometimes setLogLevel(0) will cause APP stuck if running in Xcode (because too many logs?), but setLogLevel(0)
|
|
331
|
+
// will not cause APP stuck if not running in damn Xcode, tested in https://github.com/flyskywhy/snakeRN
|
|
332
|
+
// ReactNativeBridge.GCanvasModule.setLogLevel(0); // 0 means DEBUG
|
|
333
|
+
|
|
334
|
+
// since https://github.com/flyskywhy/react-native-gcanvas/issues/44 said latest RN
|
|
335
|
+
// will warning `EventEmitter.removeListener ... Method has been deprecated` and
|
|
336
|
+
// only iOS use EventEmitter and on iOS always true in _onIsReady(), so just comment below
|
|
337
|
+
// if (Platform.OS === 'ios') {
|
|
338
|
+
// // while always true in _onIsReady(), here is just to suppress warning
|
|
339
|
+
// // on iOS Sending `GCanvasReady` with no listeners registered.
|
|
340
|
+
// const emitter = new NativeEventEmitter(ReactNativeBridge.GCanvasModule);
|
|
341
|
+
// emitter.addListener('GCanvasReady', this._onIsReady);
|
|
342
|
+
// }
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
componentWillUnmount() {
|
|
346
|
+
if (this.canvas !== null) {
|
|
347
|
+
let index = global.createCanvasElements.findIndex(canvas => canvas === this.canvas);
|
|
348
|
+
if (index >= 0) {
|
|
349
|
+
global.createCanvasElements.splice(index, 1);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (global.createCanvasElementsObj && global.createCanvasElementsObj[this.canvas.id]) {
|
|
353
|
+
delete global.createCanvasElementsObj[this.canvas.id];
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
disable(this.canvas);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// if (Platform.OS === 'ios') {
|
|
360
|
+
// const emitter = new NativeEventEmitter(ReactNativeBridge.GCanvasModule);
|
|
361
|
+
// emitter.removeListener('GCanvasReady', this._onIsReady);
|
|
362
|
+
// }
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
render() {
|
|
366
|
+
if (Platform.OS === 'web') {
|
|
367
|
+
return (
|
|
368
|
+
<View {...this.props}>
|
|
369
|
+
<Text>{'Please use <canvas> not <CanvasView> on Web'}</Text>
|
|
370
|
+
</View>
|
|
371
|
+
);
|
|
372
|
+
} else if (Platform.OS === 'harmony' && this.props.isGestureResponsible) {
|
|
373
|
+
// XComponent SURFACE 常只把 move 交给 RN PanResponder,按下/抬起丢失。
|
|
374
|
+
// 外包 RN View 收 onTouch*;同时独占 responder,避免外层 ScrollView 竖滑抢走后触发 cancel→mouseup。
|
|
375
|
+
return (
|
|
376
|
+
<View
|
|
377
|
+
style={this.props.style}
|
|
378
|
+
collapsable={false}
|
|
379
|
+
onStartShouldSetResponder={() => true}
|
|
380
|
+
onMoveShouldSetResponder={() => true}
|
|
381
|
+
onResponderTerminationRequest={() => false}
|
|
382
|
+
onTouchStart={this.onHarmonyTouchStart}
|
|
383
|
+
onTouchMove={this.onHarmonyTouchMove}
|
|
384
|
+
onTouchEnd={this.onHarmonyTouchEnd}
|
|
385
|
+
onTouchCancel={this.onHarmonyTouchEnd}>
|
|
386
|
+
<CanvasView
|
|
387
|
+
{...this.props}
|
|
388
|
+
style={{width: '100%', height: '100%'}}
|
|
389
|
+
pointerEvents="none"
|
|
390
|
+
ref={(view) => (this.refCanvasView = view)}
|
|
391
|
+
onLayout={this._onLayout}
|
|
392
|
+
onChange={this._onIsReady}
|
|
393
|
+
/>
|
|
394
|
+
</View>
|
|
395
|
+
);
|
|
396
|
+
} else {
|
|
397
|
+
return (
|
|
398
|
+
<CanvasView
|
|
399
|
+
{...this.props}
|
|
400
|
+
{...this.state.panResponder.panHandlers}
|
|
401
|
+
ref={(view) => (this.refCanvasView = view)}
|
|
402
|
+
onLayout={this._onLayout}
|
|
403
|
+
onChange={this._onIsReady}
|
|
404
|
+
/>
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|