From 0685c4f45d815964bf2a7f62b5338c6fb19e7964 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Fri, 1 Nov 2024 16:14:26 +0800 Subject: [PATCH 01/26] chore: init --- .../components/react/mpx-canvas/Bus.ts | 48 +++ .../mpx-canvas/CanvasRenderingContext2D.ts | 75 ++++ .../components/react/mpx-canvas/index.html.ts | 324 ++++++++++++++++++ .../components/react/mpx-canvas/index.tsx | 257 ++++++++++++++ .../components/react/mpx-canvas/utils.tsx | 179 ++++++++++ 5 files changed, 883 insertions(+) create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Bus.ts create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Bus.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Bus.ts new file mode 100644 index 0000000000..4b2619df36 --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Bus.ts @@ -0,0 +1,48 @@ +/** + * @typedef {Object} Message + * @property {string} id + */ + +export default class Bus { + _paused = false; + _messageListeners = {}; + _queue = []; + constructor (send) { + this._send = send + } + + post (message) { + return new Promise((resolve) => { + if (message.type !== "set") { + this._messageListeners[message.id] = resolve; + } + + if (!this._paused) { + this._send(message); + } else { + this._queue.push(message); + } + }) + } + + handle (message) { + const handler = this._messageListeners[message.id]; + delete this._messageListeners[message.id]; + + if (handler) { + handler(message); + } else { + console.warn("Received unexpected message", message); + } + } + + pause () { + this._paused = true; + } + + resume () { + this._paused = false; + this._send(this._queue); + this._queue = []; + } +} diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts new file mode 100644 index 0000000000..61b1808538 --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts @@ -0,0 +1,75 @@ +import { useEffect } from 'react' +import { useWebviewBinding } from './utils' + +const PROPERTIES = { + fillStyle: '#000', + font: '10px sans-serif', + globalAlpha: 1.0, + globalCompositeOperation: 'source-over', + lineCap: 'butt', + lineDashOffset: 0.0, + lineJoin: 'miter', + lineWidth: 1.0, + miterLimit: 10.0, + shadowBlur: 0, + shadowColor: 'rgba(0,0,0,0)', + shadowOffsetX: 0, + shadowOffsetY: 0, + strokeStyle: '#000', + textAlign: 'start', + textBaseline: 'alphabetic' +} + +const METHODS = [ + 'arc', + 'arcTo', + 'beginPath', + 'bezierCurveTo', + 'clearRect', + 'clip', + 'closePath', + 'createImageData', + 'createLinearGradient', + 'createPattern', + 'createRadialGradient', + 'drawImage', + 'fill', + 'fillRect', + 'fillText', + 'getImageData', + 'getLineDash', + 'lineTo', + 'measureText', + 'moveTo', + 'putImageData', + 'quadraticCurveTo', + 'rect', + 'restore', + 'rotate', + 'save', + 'scale', + 'setLineDash', + 'setTransform', + 'stroke', + 'strokeRect', + 'strokeText', + 'transform', + 'translate' +] + +export function useContext2D (canvas: any) { + const contextRef = useWebviewBinding( + 'context2D', + PROPERTIES, + METHODS + ) + + useEffect(() => { + if (canvas && contextRef.current) { + contextRef.current.canvas = canvas + contextRef.current.postMessage = (message: any) => canvas.postMessage(message) + } + }, [canvas]) + + return contextRef.current +} diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts new file mode 100644 index 0000000000..60e11d729a --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts @@ -0,0 +1,324 @@ +export default ` + + + + + + + + +` diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx new file mode 100644 index 0000000000..84a508a344 --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -0,0 +1,257 @@ +import React, { useRef, useState, useCallback, useEffect, forwardRef, JSX } from 'react' +import { View, Platform, StyleSheet, NativeSyntheticEvent } from 'react-native' +import { WebView } from 'react-native-webview' +import useNodesRef, { HandlerRef } from '../useNodesRef' +import { useLayout, useTransformStyle } from '../utils' +import useInnerProps from '../getInnerListeners' +import Bus from './Bus' +import { + useWebviewBinding, + constructors, + WEBVIEW_TARGET +} from './utils' +import { useContext2D } from './CanvasRenderingContext2D' +import html from './index.html' + +const stylesheet = StyleSheet.create({ + container: { overflow: 'hidden', flex: 0 }, + webview: { + overflow: 'hidden', + backgroundColor: 'transparent', + flex: 0 + }, + webviewAndroid9: { + overflow: 'hidden', + backgroundColor: 'transparent', + flex: 0, + opacity: 0.99 + } +}) + +interface CanvasProps { + style?: Record; + originWhitelist?: Array; + 'enable-var'?: boolean + 'parent-font-size'?: number + 'parent-width'?: number + 'parent-height'?: number + 'external-var-context'?: Record + bindtouchstart?: (event: NativeSyntheticEvent) => void; + bindtouchmove?: (event: NativeSyntheticEvent) => void; + bindtouchend?: (event: NativeSyntheticEvent) => void; + bindtouchcancel?: (event: NativeSyntheticEvent) => void; + bindlongtap?: (event: NativeSyntheticEvent) => void; + binderror?: (event: NativeSyntheticEvent) => void; +} + +const _Canvas = forwardRef, CanvasProps>((props: CanvasProps = {}, ref): JSX.Element => { + const { style = {}, originWhitelist = ['*'], 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props + const { width, height } = style + const [isLoaded, setIsLoaded] = useState(false) + const listenersRef = useRef([]) + const nodeRef = useRef(null) + + const canvasRef = useWebviewBinding( + 'canvas', + { width, height }, + ['toDataURL'], + [] + ) + + const { + normalStyle, + hasSelfPercent, + setWidth, + setHeight + } = useTransformStyle(style, { + enableVar, + externalVarContext, + parentFontSize, + parentWidth, + parentHeight + }) + + const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef }) + const innerProps = useInnerProps(props, { + ref: nodeRef, + style: { ...normalStyle, ...layoutStyle, ...stylesheet.container, ...{ width, height, opacity: isLoaded ? 1 : 0 }, ...style }, + ...layoutProps + }, [], { + layoutRef + }) + + const context2D = useContext2D(canvasRef.current) + + // 初始化bus和context2D + useEffect(() => { + if (canvasRef.current) { + const webviewPostMessage = (message) => { + if (canvasRef.current.webview) { + canvasRef.current.webview.postMessage(JSON.stringify(message)); + } + }; + + // 设置bus + canvasRef.current.bus = new Bus(webviewPostMessage); + canvasRef.current.bus.pause(); + + // 设置listeners数组 + canvasRef.current.listeners = []; + + // 设置addMessageListener方法 + canvasRef.current.addMessageListener = addMessageListener + + // 设置removeMessageListener方法 + canvasRef.current.removeMessageListener = removeMessageListener + + // 设置context2D + canvasRef.current.context2D = context2D; + + // 设置getContext方法 + canvasRef.current.getContext = getContext + + // 设置postMessage方法 + canvasRef.current.postMessage = postMessage + } + }, []) + + const getContext = useCallback((contextType: string) => { + if (contextType === '2d') { + return context2D + } + return null + }, []) + + const addMessageListener = useCallback((listener) => { + listenersRef.current.push(listener) + return () => removeMessageListener(listener) + }, []) + + const removeMessageListener = useCallback((listener) => { + const index = listenersRef.current.indexOf(listener) + if (index > -1) { + listenersRef.current.splice(index, 1) + } + }, []) + + const postMessage = useCallback(async (message) => { + if (!canvasRef.current?.bus) return; + + const { stack } = new Error() + const { type, payload } = await canvasRef.current.bus.post({ + id: Math.random(), + ...message + }) + + switch (type) { + case 'error': { + const error = new Error(payload.message) + error.stack = stack + throw error + } + case 'json': { + return payload + } + case 'blob': { + return atob(payload) + } + } + }, []) + + const handleMessage = useCallback((e) => { + if (!canvasRef.current) return; + + let data = JSON.parse(e.nativeEvent.data) + switch (data.type) { + case 'log': { + console.log(...data.payload) + break + } + case 'error': { + throw new Error(data.payload.message) + } + default: { + if (data.payload) { + const constructor = constructors[data.meta?.constructor]; + if (constructor) { + const { args, payload } = data + const object = constructor.constructLocally(canvasRef.current, ...args) + Object.assign(object, payload, { + [WEBVIEW_TARGET]: data.meta.target + }) + data = { + ...data, + payload: object + } + } + canvasRef.current.listeners.forEach(listener => listener(data.payload)) + } + canvasRef.current.bus.handle(data) + } + } + }, []) + + const handleLoad = useCallback(() => { + setIsLoaded(true) + if (canvasRef.current?.bus) { + canvasRef.current.bus.resume() + } + }, []) + + useNodesRef(props, ref, nodeRef, { + node: canvasRef.current + }) + + if (Platform.OS === 'android') { + const isAndroid9 = Platform.Version >= 28 + return ( + + { + if (canvasRef.current) { + canvasRef.current.webview = element + } + }} + style={[ + isAndroid9 ? stylesheet.webviewAndroid9 : stylesheet.webview, + { height, width } + ]} + source={{ html }} + originWhitelist={originWhitelist} + onMessage={handleMessage} + onLoad={handleLoad} + overScrollMode="never" + mixedContentMode="always" + scalesPageToFit={false} + javaScriptEnabled + domStorageEnabled + thirdPartyCookiesEnabled + allowUniversalAccessFromFileURLs + /> + + ) + } + + return ( + + { + if (canvasRef.current) { + canvasRef.current.webview = element + } + }} + style={[stylesheet.webview, { height, width }]} + source={{ html }} + originWhitelist={originWhitelist} + onMessage={handleMessage} + onLoad={handleLoad} + scrollEnabled={false} + /> + + ) +}) +_Canvas.displayName = 'mpxCanvas' + +export default _Canvas diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx new file mode 100644 index 0000000000..68a56de0cb --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -0,0 +1,179 @@ +import { useEffect, useRef } from 'react' + +export const WEBVIEW_TARGET = '@@WEBVIEW_TARGET' + +/** + * @mutable + */ +export const constructors = {} + +const ID = () => Math.random().toString(32).slice(2) + +/** + * 特殊构造函数配置 + */ +const SPECIAL_CONSTRUCTOR = { + ImageData: { + className: 'Uint8ClampedArray', + paramNum: 0 + } +} + +/** + * 保留原来的装饰器函数,因为它们在实例化时使用 + */ +export const webviewTarget = (targetName) => (target) => { + target.prototype[WEBVIEW_TARGET] = targetName +} + +export const webviewConstructor = (constructorName) => (target) => { + constructors[constructorName] = target + target.constructLocally = function (...args) { + return new target(...args, true) + } + + target.prototype.onConstruction = function (...args) { + if (SPECIAL_CONSTRUCTOR[constructorName] !== undefined) { + const { className, paramNum } = SPECIAL_CONSTRUCTOR[constructorName] + args[paramNum] = { className, classArgs: [args[paramNum]] } + } + this[WEBVIEW_TARGET] = ID() + this.postMessage({ + type: 'construct', + payload: { + constructor: constructorName, + id: this[WEBVIEW_TARGET], + args + } + }) + } + + target.prototype.toJSON = function () { + return { __ref__: this[WEBVIEW_TARGET] } + } +} + +export const webviewMethods = (methods) => (target) => { + for (const method of methods) { + target.prototype[method] = function (...args) { + return this.postMessage({ + type: 'exec', + payload: { + target: this[WEBVIEW_TARGET], + method, + args + } + }) + } + } +} + +export const webviewProperties = (properties) => (target) => { + for (const key of Object.keys(properties)) { + const initialValue = properties[key] + const privateKey = `__${key}__` + target.prototype[privateKey] = initialValue + Object.defineProperty(target.prototype, key, { + get () { + return this[privateKey] + }, + set (value) { + this.postMessage({ + type: 'set', + payload: { + target: this[WEBVIEW_TARGET], + key, + value + } + }) + + if (this.forceUpdate) { + this.forceUpdate() + } + + return (this[privateKey] = value) + } + }) + } +} + +export const webviewEvents = (types) => (target) => { + const { onConstruction } = target.prototype + target.prototype.onConstruction = function () { + if (onConstruction) { + onConstruction.call(this) + } + this.postMessage({ + type: 'listen', + payload: { + types, + target: this[WEBVIEW_TARGET] + } + }) + } + target.prototype.addEventListener = function (type, callback) { + this.addMessageListener((message) => { + if ( + message && + message.type === 'event' && + message.payload.target[WEBVIEW_TARGET] === this[WEBVIEW_TARGET] && + message.payload.type === type + ) { + for (const key in message.payload.target) { + const value = message.payload.target[key] + if (key in this && this[key] !== value) { + this[key] = value + } + } + callback({ + ...message.payload, + target: this + }) + } + }) + } +} + +export const useWebviewBinding = (targetName, properties = {}, methods = [], eventTypes = []) => { + const instanceRef = useRef(null) + + useEffect(() => { + if (instanceRef.current) { + // 设置target + instanceRef.current[WEBVIEW_TARGET] = targetName + + // 设置properties + Object.entries(properties).forEach(([key, initialValue]) => { + const privateKey = `__${key}__` + instanceRef.current[privateKey] = initialValue + }) + + // 设置methods + methods.forEach(method => { + instanceRef.current[method] = (...args) => { + return instanceRef.current.postMessage({ + type: 'exec', + payload: { + target: instanceRef.current[WEBVIEW_TARGET], + method, + args + } + }) + } + }) + + // 设置events + if (eventTypes.length > 0) { + instanceRef.current.postMessage({ + type: 'listen', + payload: { + types: eventTypes, + target: instanceRef.current[WEBVIEW_TARGET] + } + }) + } + } + }, [targetName, properties, methods, eventTypes]) + + return instanceRef +} From 68b8dc6931de7e54b0968c92c479253be735e58a Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Mon, 4 Nov 2024 11:41:51 +0800 Subject: [PATCH 02/26] chore: lint --- .../components/react/mpx-canvas/Bus.ts | 52 +++-- .../components/react/mpx-canvas/index.tsx | 30 +-- .../components/react/mpx-canvas/utils.tsx | 218 +++++++----------- 3 files changed, 127 insertions(+), 173 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Bus.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Bus.ts index 4b2619df36..4cb1eb4df9 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Bus.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Bus.ts @@ -1,48 +1,50 @@ -/** - * @typedef {Object} Message - * @property {string} id - */ - +interface Message { + id?: string + type: string + payload?: any +} export default class Bus { - _paused = false; - _messageListeners = {}; - _queue = []; - constructor (send) { + _paused: Boolean = false; + _messageListeners: { [key: string]: (message: Message) => void } = {} + _queue: Message[] = [] + _send: (message: Message | Message[]) => void + constructor (send: (message: Message | Message[]) => void) { this._send = send } - post (message) { + post (message: Message): Promise { return new Promise((resolve) => { - if (message.type !== "set") { - this._messageListeners[message.id] = resolve; + if (message.type !== 'set' && message.id) { + this._messageListeners[message.id] = resolve } if (!this._paused) { - this._send(message); + this._send(message) } else { - this._queue.push(message); + this._queue.push(message) } }) } - handle (message) { - const handler = this._messageListeners[message.id]; - delete this._messageListeners[message.id]; + handle (message: Message): void { + if (!message.id) return + const handler = this._messageListeners[message.id] + delete this._messageListeners[message.id] if (handler) { - handler(message); + handler(message) } else { - console.warn("Received unexpected message", message); + console.warn('Received unexpected message', message) } } - pause () { - this._paused = true; + pause (): void { + this._paused = true } - resume () { - this._paused = false; - this._send(this._queue); - this._queue = []; + resume (): void { + this._paused = false + this._send(this._queue) + this._queue = [] } } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index 84a508a344..497a59dc91 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState, useCallback, useEffect, forwardRef, JSX } from 'react' +import React, { useRef, useState, useCallback, useEffect, forwardRef, JSX, TouchEvent } from 'react' import { View, Platform, StyleSheet, NativeSyntheticEvent } from 'react-native' import { WebView } from 'react-native-webview' import useNodesRef, { HandlerRef } from '../useNodesRef' @@ -10,7 +10,7 @@ import { constructors, WEBVIEW_TARGET } from './utils' -import { useContext2D } from './CanvasRenderingContext2D' +import { useContext2D } from './canvasRenderingContext2D' import html from './index.html' const stylesheet = StyleSheet.create({ @@ -87,16 +87,16 @@ const _Canvas = forwardRef, CanvasPr if (canvasRef.current) { const webviewPostMessage = (message) => { if (canvasRef.current.webview) { - canvasRef.current.webview.postMessage(JSON.stringify(message)); + canvasRef.current.webview.postMessage(JSON.stringify(message)) } - }; + } // 设置bus - canvasRef.current.bus = new Bus(webviewPostMessage); - canvasRef.current.bus.pause(); + canvasRef.current.bus = new Bus(webviewPostMessage) + canvasRef.current.bus.pause() // 设置listeners数组 - canvasRef.current.listeners = []; + canvasRef.current.listeners = [] // 设置addMessageListener方法 canvasRef.current.addMessageListener = addMessageListener @@ -105,7 +105,7 @@ const _Canvas = forwardRef, CanvasPr canvasRef.current.removeMessageListener = removeMessageListener // 设置context2D - canvasRef.current.context2D = context2D; + canvasRef.current.context2D = context2D // 设置getContext方法 canvasRef.current.getContext = getContext @@ -135,7 +135,7 @@ const _Canvas = forwardRef, CanvasPr }, []) const postMessage = useCallback(async (message) => { - if (!canvasRef.current?.bus) return; + if (!canvasRef.current?.bus) return const { stack } = new Error() const { type, payload } = await canvasRef.current.bus.post({ @@ -159,7 +159,7 @@ const _Canvas = forwardRef, CanvasPr }, []) const handleMessage = useCallback((e) => { - if (!canvasRef.current) return; + if (!canvasRef.current) return let data = JSON.parse(e.nativeEvent.data) switch (data.type) { @@ -172,7 +172,7 @@ const _Canvas = forwardRef, CanvasPr } default: { if (data.payload) { - const constructor = constructors[data.meta?.constructor]; + const constructor = constructors[data.meta?.constructor] if (constructor) { const { args, payload } = data const object = constructor.constructLocally(canvasRef.current, ...args) @@ -208,10 +208,10 @@ const _Canvas = forwardRef, CanvasPr { - if (canvasRef.current) { - canvasRef.current.webview = element - } - }} + if (canvasRef.current) { + canvasRef.current.webview = element + } + }} style={[ isAndroid9 ? stylesheet.webviewAndroid9 : stylesheet.webview, { height, width } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx index 68a56de0cb..98682ec9f5 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -2,176 +2,128 @@ import { useEffect, useRef } from 'react' export const WEBVIEW_TARGET = '@@WEBVIEW_TARGET' -/** - * @mutable - */ -export const constructors = {} - -const ID = () => Math.random().toString(32).slice(2) - -/** - * 特殊构造函数配置 - */ -const SPECIAL_CONSTRUCTOR = { - ImageData: { - className: 'Uint8ClampedArray', - paramNum: 0 - } +export const constructors: Record = {} + +interface WebviewInstance { + [WEBVIEW_TARGET]: string + [key: string]: any + postMessage: (message: WebviewMessage) => void + forceUpdate?: () => void + addMessageListener: (listener: MessageListener) => void } -/** - * 保留原来的装饰器函数,因为它们在实例化时使用 - */ -export const webviewTarget = (targetName) => (target) => { - target.prototype[WEBVIEW_TARGET] = targetName -} - -export const webviewConstructor = (constructorName) => (target) => { - constructors[constructorName] = target - target.constructLocally = function (...args) { - return new target(...args, true) - } - - target.prototype.onConstruction = function (...args) { - if (SPECIAL_CONSTRUCTOR[constructorName] !== undefined) { - const { className, paramNum } = SPECIAL_CONSTRUCTOR[constructorName] - args[paramNum] = { className, classArgs: [args[paramNum]] } - } - this[WEBVIEW_TARGET] = ID() - this.postMessage({ - type: 'construct', - payload: { - constructor: constructorName, - id: this[WEBVIEW_TARGET], - args - } - }) - } - - target.prototype.toJSON = function () { - return { __ref__: this[WEBVIEW_TARGET] } +interface WebviewMessage { + type: 'set' | 'exec' | 'listen' | 'event' + payload: { + target?: string | { [WEBVIEW_TARGET]: string, [key: string]: any } + key?: string + value?: any + method?: string + args?: any[] + types?: string[] + type?: string } } -export const webviewMethods = (methods) => (target) => { - for (const method of methods) { - target.prototype[method] = function (...args) { - return this.postMessage({ - type: 'exec', - payload: { - target: this[WEBVIEW_TARGET], - method, - args - } - }) - } - } +type MessageListener = (message: WebviewMessage) => void + +const setupWebviewTarget = (instance: WebviewInstance, targetName: string): void => { + instance[WEBVIEW_TARGET] = targetName } -export const webviewProperties = (properties) => (target) => { - for (const key of Object.keys(properties)) { - const initialValue = properties[key] +const setupWebviewProperties = (instance: WebviewInstance, properties: Record): void => { + Object.entries(properties).forEach(([key, initialValue]) => { const privateKey = `__${key}__` - target.prototype[privateKey] = initialValue - Object.defineProperty(target.prototype, key, { + instance[privateKey] = initialValue + Object.defineProperty(instance, key, { get () { - return this[privateKey] + return instance[privateKey] }, set (value) { - this.postMessage({ + instance.postMessage({ type: 'set', payload: { - target: this[WEBVIEW_TARGET], + target: instance[WEBVIEW_TARGET], key, value } }) - if (this.forceUpdate) { - this.forceUpdate() + if (instance.forceUpdate) { + instance.forceUpdate() } - return (this[privateKey] = value) + return (instance[privateKey] = value) } }) - } + }) } -export const webviewEvents = (types) => (target) => { - const { onConstruction } = target.prototype - target.prototype.onConstruction = function () { - if (onConstruction) { - onConstruction.call(this) +const setupWebviewMethods = (instance: WebviewInstance, methods: string[]): void => { + methods.forEach(method => { + instance[method] = (...args: any[]) => { + return instance.postMessage({ + type: 'exec', + payload: { + target: instance[WEBVIEW_TARGET], + method, + args + } + }) } - this.postMessage({ + }) +} + +const setupWebviewEvents = (instance: WebviewInstance, eventTypes: string[]): void => { + if (eventTypes.length > 0) { + instance.postMessage({ type: 'listen', payload: { - types, - target: this[WEBVIEW_TARGET] + types: eventTypes, + target: instance[WEBVIEW_TARGET] } }) - } - target.prototype.addEventListener = function (type, callback) { - this.addMessageListener((message) => { - if ( - message && - message.type === 'event' && - message.payload.target[WEBVIEW_TARGET] === this[WEBVIEW_TARGET] && - message.payload.type === type - ) { - for (const key in message.payload.target) { - const value = message.payload.target[key] - if (key in this && this[key] !== value) { - this[key] = value + + instance.addEventListener = function (type: string, callback: (event: any) => void) { + this.addMessageListener((message: WebviewMessage) => { + if ( + message && + message.type === 'event' && + message.payload.target && + typeof message.payload.target === 'object' && + message.payload.target[WEBVIEW_TARGET] === this[WEBVIEW_TARGET] && + message.payload.type === type + ) { + for (const key in message.payload.target) { + const value = message.payload.target[key] + if (key in this && this[key] !== value) { + this[key] = value + } } + callback({ + ...message.payload, + target: this + }) } - callback({ - ...message.payload, - target: this - }) - } - }) + }) + } } } -export const useWebviewBinding = (targetName, properties = {}, methods = [], eventTypes = []) => { - const instanceRef = useRef(null) +export const useWebviewBinding = ( + targetName: string, + properties: Record = {}, + methods: string[] = [], + eventTypes: string[] = [] +) => { + const instanceRef = useRef(null) useEffect(() => { if (instanceRef.current) { - // 设置target - instanceRef.current[WEBVIEW_TARGET] = targetName - - // 设置properties - Object.entries(properties).forEach(([key, initialValue]) => { - const privateKey = `__${key}__` - instanceRef.current[privateKey] = initialValue - }) - - // 设置methods - methods.forEach(method => { - instanceRef.current[method] = (...args) => { - return instanceRef.current.postMessage({ - type: 'exec', - payload: { - target: instanceRef.current[WEBVIEW_TARGET], - method, - args - } - }) - } - }) - - // 设置events - if (eventTypes.length > 0) { - instanceRef.current.postMessage({ - type: 'listen', - payload: { - types: eventTypes, - target: instanceRef.current[WEBVIEW_TARGET] - } - }) - } + setupWebviewTarget(instanceRef.current, targetName) + setupWebviewProperties(instanceRef.current, properties) + setupWebviewMethods(instanceRef.current, methods) + setupWebviewEvents(instanceRef.current, eventTypes) } }, [targetName, properties, methods, eventTypes]) From 5e857400723904ae7f251e74bb7161ef1bfef4f8 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Tue, 5 Nov 2024 15:29:28 +0800 Subject: [PATCH 03/26] fix: error --- .../lib/runtime/components/react/mpx-canvas/index.tsx | 3 --- .../lib/runtime/components/react/mpx-canvas/utils.tsx | 5 +++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index 497a59dc91..0cc57bca56 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -84,7 +84,6 @@ const _Canvas = forwardRef, CanvasPr // 初始化bus和context2D useEffect(() => { - if (canvasRef.current) { const webviewPostMessage = (message) => { if (canvasRef.current.webview) { canvasRef.current.webview.postMessage(JSON.stringify(message)) @@ -112,7 +111,6 @@ const _Canvas = forwardRef, CanvasPr // 设置postMessage方法 canvasRef.current.postMessage = postMessage - } }, []) const getContext = useCallback((contextType: string) => { @@ -159,7 +157,6 @@ const _Canvas = forwardRef, CanvasPr }, []) const handleMessage = useCallback((e) => { - if (!canvasRef.current) return let data = JSON.parse(e.nativeEvent.data) switch (data.type) { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx index 98682ec9f5..57a3d0587c 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -36,6 +36,8 @@ const setupWebviewProperties = (instance: WebviewInstance, properties: Record { - const instanceRef = useRef(null) + const instanceRef = useRef({}) useEffect(() => { if (instanceRef.current) { From d684e9dbc1953b9a0ad6a6e031d6a1bb3d44f58a Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Wed, 6 Nov 2024 19:26:56 +0800 Subject: [PATCH 04/26] =?UTF-8?q?chore:=20=E5=88=A0=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=94=A8code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/react/mpx-canvas/Bus.ts | 3 +- .../mpx-canvas/CanvasRenderingContext2D.ts | 10 +- .../components/react/mpx-canvas/index.html.ts | 34 ----- .../components/react/mpx-canvas/index.tsx | 138 ++++++++---------- .../components/react/mpx-canvas/utils.tsx | 58 ++------ 5 files changed, 81 insertions(+), 162 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Bus.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Bus.ts index 4cb1eb4df9..4a6937384f 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Bus.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Bus.ts @@ -1,3 +1,4 @@ +import { warn } from '@mpxjs/utils' interface Message { id?: string type: string @@ -34,7 +35,7 @@ export default class Bus { if (handler) { handler(message) } else { - console.warn('Received unexpected message', message) + warn(`Received unexpected message: ${message}`) } } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts index 61b1808538..5ff325024e 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts @@ -21,6 +21,7 @@ const PROPERTIES = { } const METHODS = [ + // draw、createCircularGradient、setFillStyle、 setFontSize、 setGlobalAlpha、 setLineCap、setLineJoin、setLineWidth、setMiterLimit、setShadow、setStrokeStyle、 setTextAlign、setTextBaseline 不支持 'arc', 'arcTo', 'beginPath', @@ -58,10 +59,11 @@ const METHODS = [ ] export function useContext2D (canvas: any) { - const contextRef = useWebviewBinding( - 'context2D', - PROPERTIES, - METHODS + const contextRef = useWebviewBinding({ + targetName: 'context2D', + properties: PROPERTIES, + methods: METHODS + } ) useEffect(() => { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts index 60e11d729a..99eb22f5a7 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts @@ -247,40 +247,6 @@ function handleMessage(_a) { targets[target][key] = populateRefs(value); break; } - case "construct": { - var constructor = payload.constructor, target = payload.id, _d = payload.args, args = _d === void 0 ? [] : _d; - var newArgs = createObjectsFromArgs(args); - var object = void 0; - try { - object = new ((_c = constructors[constructor]).bind.apply(_c, __spreadArray([void 0], newArgs, false)))(); - } - catch (error) { - throw new Error("Error while constructing ".concat(constructor, " ").concat(error.message)); - } - object.__constructorName__ = constructor; - var message = toMessage({}); - targets[target] = object; - window.ReactNativeWebView.postMessage(JSON.stringify(__assign({ id: id }, message))); - break; - } - case "listen": { - var types = payload.types, target_1 = payload.target; - for (var _i = 0, types_1 = types; _i < types_1.length; _i++) { - var eventType = types_1[_i]; - targets[target_1].addEventListener(eventType, function (e) { - var _a; - var message = toMessage({ - type: "event", - payload: { - type: e.type, - target: __assign(__assign({}, flattenObject(targets[target_1])), (_a = {}, _a[WEBVIEW_TARGET] = target_1, _a)), - }, - }); - window.ReactNativeWebView.postMessage(JSON.stringify(__assign({ id: id }, message))); - }); - } - break; - } } } var handleError = function (err, message) { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index 0cc57bca56..a52d6ef08d 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -1,14 +1,23 @@ +/** + * ✘ type + * ✘ canvas-id + * ✘ disable-scroll + * ✔ bindtouchstart + * ✔ bindtouchmove + * ✔ bindtouchend + * ✔ bindtouchcancel + * ✔ bindlongtap + * ✔ binderror + */ import React, { useRef, useState, useCallback, useEffect, forwardRef, JSX, TouchEvent } from 'react' import { View, Platform, StyleSheet, NativeSyntheticEvent } from 'react-native' import { WebView } from 'react-native-webview' import useNodesRef, { HandlerRef } from '../useNodesRef' import { useLayout, useTransformStyle } from '../utils' -import useInnerProps from '../getInnerListeners' +import useInnerProps, { getCustomEvent } from '../getInnerListeners' import Bus from './Bus' import { - useWebviewBinding, - constructors, - WEBVIEW_TARGET + useWebviewBinding } from './utils' import { useContext2D } from './canvasRenderingContext2D' import html from './index.html' @@ -48,15 +57,13 @@ const _Canvas = forwardRef, CanvasPr const { style = {}, originWhitelist = ['*'], 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props const { width, height } = style const [isLoaded, setIsLoaded] = useState(false) - const listenersRef = useRef([]) const nodeRef = useRef(null) - const canvasRef = useWebviewBinding( - 'canvas', - { width, height }, - ['toDataURL'], - [] - ) + const canvasRef = useWebviewBinding({ + targetName: 'canvas', + properties: { width, height }, + methods: ['toDataURL'] + }) const { normalStyle, @@ -84,33 +91,24 @@ const _Canvas = forwardRef, CanvasPr // 初始化bus和context2D useEffect(() => { - const webviewPostMessage = (message) => { - if (canvasRef.current.webview) { - canvasRef.current.webview.postMessage(JSON.stringify(message)) - } + const webviewPostMessage = (message) => { + if (canvasRef.current.webview) { + canvasRef.current.webview.postMessage(JSON.stringify(message)) } + } - // 设置bus - canvasRef.current.bus = new Bus(webviewPostMessage) - canvasRef.current.bus.pause() - - // 设置listeners数组 - canvasRef.current.listeners = [] - - // 设置addMessageListener方法 - canvasRef.current.addMessageListener = addMessageListener - - // 设置removeMessageListener方法 - canvasRef.current.removeMessageListener = removeMessageListener + // 设置bus + canvasRef.current.bus = new Bus(webviewPostMessage) + canvasRef.current.bus.pause() - // 设置context2D - canvasRef.current.context2D = context2D + // 设置context2D + canvasRef.current.context2D = context2D - // 设置getContext方法 - canvasRef.current.getContext = getContext + // 设置getContext方法 + canvasRef.current.getContext = getContext - // 设置postMessage方法 - canvasRef.current.postMessage = postMessage + // 设置postMessage方法 + canvasRef.current.postMessage = postMessage }, []) const getContext = useCallback((contextType: string) => { @@ -120,22 +118,8 @@ const _Canvas = forwardRef, CanvasPr return null }, []) - const addMessageListener = useCallback((listener) => { - listenersRef.current.push(listener) - return () => removeMessageListener(listener) - }, []) - - const removeMessageListener = useCallback((listener) => { - const index = listenersRef.current.indexOf(listener) - if (index > -1) { - listenersRef.current.splice(index, 1) - } - }, []) - const postMessage = useCallback(async (message) => { if (!canvasRef.current?.bus) return - - const { stack } = new Error() const { type, payload } = await canvasRef.current.bus.post({ id: Math.random(), ...message @@ -143,9 +127,17 @@ const _Canvas = forwardRef, CanvasPr switch (type) { case 'error': { - const error = new Error(payload.message) - error.stack = stack - throw error + const { binderror } = props + binderror && + binderror( + getCustomEvent('error', {}, { + detail: { + errMsg: payload.message + }, + layoutRef + }, props) + ) + break } case 'json': { return payload @@ -156,39 +148,29 @@ const _Canvas = forwardRef, CanvasPr } }, []) - const handleMessage = useCallback((e) => { - - let data = JSON.parse(e.nativeEvent.data) + const onMessage = useCallback((e) => { + const data = JSON.parse(e.nativeEvent.data) switch (data.type) { - case 'log': { - console.log(...data.payload) - break - } case 'error': { - throw new Error(data.payload.message) + const { binderror } = props + binderror && + binderror( + getCustomEvent('error', e, { + detail: { + errMsg: data.payload.message + }, + layoutRef + }, props) + ) + break } default: { - if (data.payload) { - const constructor = constructors[data.meta?.constructor] - if (constructor) { - const { args, payload } = data - const object = constructor.constructLocally(canvasRef.current, ...args) - Object.assign(object, payload, { - [WEBVIEW_TARGET]: data.meta.target - }) - data = { - ...data, - payload: object - } - } - canvasRef.current.listeners.forEach(listener => listener(data.payload)) - } canvasRef.current.bus.handle(data) } } }, []) - const handleLoad = useCallback(() => { + const onLoad = useCallback(() => { setIsLoaded(true) if (canvasRef.current?.bus) { canvasRef.current.bus.resume() @@ -215,8 +197,8 @@ const _Canvas = forwardRef, CanvasPr ]} source={{ html }} originWhitelist={originWhitelist} - onMessage={handleMessage} - onLoad={handleLoad} + onMessage={onMessage} + onLoad={onLoad} overScrollMode="never" mixedContentMode="always" scalesPageToFit={false} @@ -242,8 +224,8 @@ const _Canvas = forwardRef, CanvasPr style={[stylesheet.webview, { height, width }]} source={{ html }} originWhitelist={originWhitelist} - onMessage={handleMessage} - onLoad={handleLoad} + onMessage={onMessage} + onLoad={onLoad} scrollEnabled={false} /> diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx index 57a3d0587c..648b4af550 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef } from 'react' export const WEBVIEW_TARGET = '@@WEBVIEW_TARGET' -export const constructors: Record = {} +export const constructors = {} interface WebviewInstance { [WEBVIEW_TARGET]: string @@ -75,48 +75,17 @@ const setupWebviewMethods = (instance: WebviewInstance, methods: string[]): void }) } -const setupWebviewEvents = (instance: WebviewInstance, eventTypes: string[]): void => { - if (eventTypes.length > 0) { - instance.postMessage({ - type: 'listen', - payload: { - types: eventTypes, - target: instance[WEBVIEW_TARGET] - } - }) - - instance.addEventListener = function (type: string, callback: (event: any) => void) { - this.addMessageListener((message: WebviewMessage) => { - if ( - message && - message.type === 'event' && - message.payload.target && - typeof message.payload.target === 'object' && - message.payload.target[WEBVIEW_TARGET] === this[WEBVIEW_TARGET] && - message.payload.type === type - ) { - for (const key in message.payload.target) { - const value = message.payload.target[key] - if (key in this && this[key] !== value) { - this[key] = value - } - } - callback({ - ...message.payload, - target: this - }) - } - }) - } - } -} - -export const useWebviewBinding = ( - targetName: string, - properties: Record = {}, - methods: string[] = [], - eventTypes: string[] = [] -) => { +export const useWebviewBinding = ({ + targetName, + properties = {}, + methods = [], + constructorName = '' +}: { + targetName: string; + properties?: Record; + methods?: string[]; + constructorName?: string +}) => { const instanceRef = useRef({}) useEffect(() => { @@ -124,9 +93,8 @@ export const useWebviewBinding = ( setupWebviewTarget(instanceRef.current, targetName) setupWebviewProperties(instanceRef.current, properties) setupWebviewMethods(instanceRef.current, methods) - setupWebviewEvents(instanceRef.current, eventTypes) } - }, [targetName, properties, methods, eventTypes]) + }, [targetName, properties, methods]) return instanceRef } From 8382c018a93a2491c36fcbe59e827997b6819bfd Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Thu, 7 Nov 2024 17:26:35 +0800 Subject: [PATCH 05/26] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81CanvasGradient?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../react/mpx-canvas/CanvasGradient.ts | 32 +++++++++++++++++++ .../components/react/mpx-canvas/index.html.ts | 23 +------------ .../components/react/mpx-canvas/index.tsx | 25 +++++++++++++-- .../components/react/mpx-canvas/utils.tsx | 2 +- 4 files changed, 56 insertions(+), 26 deletions(-) create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts new file mode 100644 index 0000000000..06b24ca1c0 --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts @@ -0,0 +1,32 @@ +import { WEBVIEW_TARGET, constructors } from './utils' + +export default class CanvasGradient { + private canvas: any; + [WEBVIEW_TARGET]?: string; + + constructor (canvas: any) { + this.canvas = canvas + } + postMessage = (message: any) => { + return this.canvas.postMessage(message) + } + toJSON () { + return { __ref__: this[WEBVIEW_TARGET] }; + } + addColorStop (offset: number, color: string) { + return this.postMessage({ + type: 'exec', + payload: { + target: this[WEBVIEW_TARGET], + method: 'addColorStop', + args: [offset, color] + } + }) + } + + // 添加静态方法用于本地构造实例 + static constructLocally (canvas: any, ...args: any[]) { + return new CanvasGradient(canvas, ...args, true) + } +} +constructors.CanvasGradient = CanvasGradient \ No newline at end of file diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts index 99eb22f5a7..58798eab65 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts @@ -180,26 +180,12 @@ var toArgs = function (result) { var args = []; for (var key in result) { if (result[key] !== undefined && key !== "@@WEBVIEW_TARGET") { - if (typedArrays[result[key].constructor.name] !== undefined) { - result[key] = Array.from(result[key]); - } args.push(result[key]); } } return args; }; -var createObjectsFromArgs = function (args) { - var _a; - for (var index = 0; index < args.length; index += 1) { - var currentArg = args[index]; - if (currentArg && currentArg.className !== undefined) { - var className = currentArg.className, classArgs = currentArg.classArgs; - var object = new ((_a = constructors[className]).bind.apply(_a, __spreadArray([void 0], classArgs, false)))(); - args[index] = object; - } - } - return args; -}; + var canvas = document.createElement("canvas"); var autoScaledCanvas = new AutoScaledCanvas(canvas); var targets = { @@ -207,14 +193,7 @@ var targets = { context2D: canvas.getContext("2d"), }; var constructors = { - Image: Image, - Path2D: Path2D, CanvasGradient: CanvasGradient, - ImageData: ImageData, - Uint8ClampedArray: Uint8ClampedArray, -}; -var typedArrays = { - Uint8ClampedArray: Uint8ClampedArray, }; var populateRefs = function (arg) { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index a52d6ef08d..936dec4f13 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -17,10 +17,13 @@ import { useLayout, useTransformStyle } from '../utils' import useInnerProps, { getCustomEvent } from '../getInnerListeners' import Bus from './Bus' import { - useWebviewBinding + useWebviewBinding, + constructors, + WEBVIEW_TARGET } from './utils' -import { useContext2D } from './canvasRenderingContext2D' +import { useContext2D } from './CanvasRenderingContext2D' import html from './index.html' +import './CanvasGradient' const stylesheet = StyleSheet.create({ container: { overflow: 'hidden', flex: 0 }, @@ -149,7 +152,7 @@ const _Canvas = forwardRef, CanvasPr }, []) const onMessage = useCallback((e) => { - const data = JSON.parse(e.nativeEvent.data) + let data = JSON.parse(e.nativeEvent.data) switch (data.type) { case 'error': { const { binderror } = props @@ -165,6 +168,22 @@ const _Canvas = forwardRef, CanvasPr break } default: { + if (data.payload) { + // createLinearGradient 方法调用需要在 constructors 中需要注册 CanvasGradient + const constructor = constructors[data.meta.constructor] + if (constructor) { + const { args, payload } = data + // RN 端同步生成一个 CanvasGradient 的实例 + const object = constructor.constructLocally(canvasRef.current, ...args) + Object.assign(object, payload, { + [WEBVIEW_TARGET]: data.meta.target + }) + data = { + ...data, + payload: object + } + } + } canvasRef.current.bus.handle(data) } } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx index 648b4af550..98bf80a9dd 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef } from 'react' export const WEBVIEW_TARGET = '@@WEBVIEW_TARGET' -export const constructors = {} +export const constructors: Record = {} interface WebviewInstance { [WEBVIEW_TARGET]: string From 00613fc4c67deea8cafd370d52a10b883cdb8ce4 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Mon, 11 Nov 2024 11:58:46 +0800 Subject: [PATCH 06/26] =?UTF-8?q?fix:=20toDataURL=20payload=20=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/mpx-canvas/OffscreenCanvas.ts | 0 .../lib/runtime/components/react/mpx-canvas/index.html.ts | 2 +- .../lib/runtime/components/react/mpx-canvas/utils.tsx | 3 +-- 3 files changed, 2 insertions(+), 3 deletions(-) create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/OffscreenCanvas.ts diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/OffscreenCanvas.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/OffscreenCanvas.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts index 58798eab65..c7c3449f98 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts @@ -172,7 +172,7 @@ var toMessage = function (result) { } return { type: "json", - payload: JSON.stringify(result), + payload: typeof result === 'string' ? result : JSON.stringify(result), meta: {}, }; }; diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx index 98bf80a9dd..b4509c6223 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -78,8 +78,7 @@ const setupWebviewMethods = (instance: WebviewInstance, methods: string[]): void export const useWebviewBinding = ({ targetName, properties = {}, - methods = [], - constructorName = '' + methods = [] }: { targetName: string; properties?: Record; From 2fecb46c109c80f90cf5cf07c0348f19cac32c20 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Mon, 11 Nov 2024 12:02:06 +0800 Subject: [PATCH 07/26] =?UTF-8?q?feat:=20=E7=BC=96=E8=AF=91=E6=97=B6?= =?UTF-8?q?=E6=94=AF=E6=8C=81canvas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/platform/template/wx/component-config/canvas.js | 8 ++++++++ .../platform/template/wx/component-config/unsupported.js | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/platform/template/wx/component-config/canvas.js b/packages/webpack-plugin/lib/platform/template/wx/component-config/canvas.js index 2e288c0ab2..80fd912d16 100644 --- a/packages/webpack-plugin/lib/platform/template/wx/component-config/canvas.js +++ b/packages/webpack-plugin/lib/platform/template/wx/component-config/canvas.js @@ -8,6 +8,14 @@ module.exports = function ({ print }) { const qaEventLog = print({ platform: 'qa', tag: TAG_NAME, isError: false, type: 'event' }) return { test: TAG_NAME, + android (tag, { el }) { + el.isBuiltIn = true + return 'mpx-canvas' + }, + ios (tag, { el }) { + el.isBuiltIn = true + return 'mpx-canvas' + }, props: [ { test: /^canvas-id$/, diff --git a/packages/webpack-plugin/lib/platform/template/wx/component-config/unsupported.js b/packages/webpack-plugin/lib/platform/template/wx/component-config/unsupported.js index 3a05d72fad..afe24bb48a 100644 --- a/packages/webpack-plugin/lib/platform/template/wx/component-config/unsupported.js +++ b/packages/webpack-plugin/lib/platform/template/wx/component-config/unsupported.js @@ -11,7 +11,7 @@ const JD_UNSUPPORTED_TAG_NAME_ARR = ['functional-page-navigator', 'live-pusher', // 快应用不支持的标签集合 const QA_UNSUPPORTED_TAG_NAME_ARR = ['movable-view', 'movable-area', 'open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'cover-image'] // RN不支持的标签集合 -const RN_UNSUPPORTED_TAG_NAME_ARR = ['open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'progress', 'rich-text', 'slider', 'audio', 'camera', 'video', 'canvas', 'match-media', 'page-container', 'editor', 'keyboard-accessory', 'map'] +const RN_UNSUPPORTED_TAG_NAME_ARR = ['open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'progress', 'rich-text', 'slider', 'audio', 'camera', 'video', 'match-media', 'page-container', 'editor', 'keyboard-accessory', 'map'] /** * @param {function(object): function} print From e9615972ec1b4e47f190458eb395d91b9357a376 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Mon, 11 Nov 2024 18:59:22 +0800 Subject: [PATCH 08/26] =?UTF-8?q?chore:=20=E4=BF=AE=E6=94=B9=E5=8C=85?= =?UTF-8?q?=E8=A3=85=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../react/mpx-canvas/CanvasGradient.ts | 25 ++------ .../mpx-canvas/CanvasRenderingContext2D.ts | 28 ++++----- .../components/react/mpx-canvas/Image.ts | 59 +++++++++++++++++++ .../components/react/mpx-canvas/index.html.ts | 7 ++- .../components/react/mpx-canvas/index.tsx | 19 ++++-- .../components/react/mpx-canvas/utils.tsx | 33 ++++++++++- 6 files changed, 123 insertions(+), 48 deletions(-) create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts index 06b24ca1c0..6459b08c56 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts @@ -1,32 +1,15 @@ -import { WEBVIEW_TARGET, constructors } from './utils' - +import { WEBVIEW_TARGET, constructors, setupWebviewMethods, setupWebviewConstructor } from './utils' +const METHODS = ['addColorStop'] export default class CanvasGradient { private canvas: any; [WEBVIEW_TARGET]?: string; constructor (canvas: any) { this.canvas = canvas + setupWebviewMethods(this, METHODS) + setupWebviewConstructor(this, 'CanvasGradient') } postMessage = (message: any) => { return this.canvas.postMessage(message) } - toJSON () { - return { __ref__: this[WEBVIEW_TARGET] }; - } - addColorStop (offset: number, color: string) { - return this.postMessage({ - type: 'exec', - payload: { - target: this[WEBVIEW_TARGET], - method: 'addColorStop', - args: [offset, color] - } - }) - } - - // 添加静态方法用于本地构造实例 - static constructLocally (canvas: any, ...args: any[]) { - return new CanvasGradient(canvas, ...args, true) - } } -constructors.CanvasGradient = CanvasGradient \ No newline at end of file diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts index 5ff325024e..0bf8efba2a 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts @@ -1,5 +1,4 @@ -import { useEffect } from 'react' -import { useWebviewBinding } from './utils' +import { setupWebviewProperties, setupWebviewMethods, setupWebviewTarget } from './utils' const PROPERTIES = { fillStyle: '#000', @@ -57,21 +56,16 @@ const METHODS = [ 'transform', 'translate' ] - -export function useContext2D (canvas: any) { - const contextRef = useWebviewBinding({ - targetName: 'context2D', - properties: PROPERTIES, - methods: METHODS +export default class CanvasRenderingContext2D { + canvas: any + constructor(canvas) { + this.canvas = canvas + setupWebviewTarget(this, 'context2D') + setupWebviewProperties(this, PROPERTIES) + setupWebviewMethods(this, METHODS) } - ) - - useEffect(() => { - if (canvas && contextRef.current) { - contextRef.current.canvas = canvas - contextRef.current.postMessage = (message: any) => canvas.postMessage(message) - } - }, [canvas]) - return contextRef.current + postMessage(message) { + return this.canvas.postMessage(message) + } } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts new file mode 100644 index 0000000000..ff0c77569e --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts @@ -0,0 +1,59 @@ +import { WEBVIEW_TARGET, constructors, ID, setupWebviewProperties } from './utils' +import { warn } from '@mpxjs/utils' + +export class Image { + canvas: any; + width: Number; + height: Number; + constructor (canvas: any, width: Number, height: Number, noOnConstruction?: Boolean) { + if (!(canvas instanceof Canvas)) { + warn('Image must be initialized with a Canvas instance') + } + this.canvas = canvas + if (width) { + this.width = width + } + if (height) { + this.height = height + } + if (!noOnConstruction) { + this.onConstruction() + } + setupWebviewProperties(this, { + crossOrigin: undefined, + height: undefined, + src: undefined, + width: undefined + }) + } + + postMessage = (message: any) => { + return this.canvas.postMessage(message) + } + + // 添加静态方法用于本地构造实例 + static constructLocally (canvas: any, ...args: any[]) { + return new Image(canvas, ...args, true) + } + + onConstruction (...args) { + this[WEBVIEW_TARGET] = ID() + this.postMessage({ + type: 'construct', + payload: { + constructor: 'Image', + id: this[WEBVIEW_TARGET], + args + } + }) + } + + toJSON () { + return { __ref__: this[WEBVIEW_TARGET] } + } +} + +export function createImage (canvas, height, width) { + return new Image(canvas, height, width) +} +constructors.Image = Image diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts index c7c3449f98..3c113e6961 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts @@ -194,8 +194,13 @@ var targets = { }; var constructors = { CanvasGradient: CanvasGradient, + Image: Image }; - +Image.bind = + Image.bind || + function () { + return Image; + }; var populateRefs = function (arg) { if (arg && arg.__ref__) { return targets[arg.__ref__]; diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index 936dec4f13..ac69192e05 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -21,9 +21,10 @@ import { constructors, WEBVIEW_TARGET } from './utils' -import { useContext2D } from './CanvasRenderingContext2D' -import html from './index.html' +import CanvasRenderingContext2D from './CanvasRenderingContext2D' +import html from './index.html.ts' import './CanvasGradient' +import { createImage as canvasCreateImage } from './Image' const stylesheet = StyleSheet.create({ container: { overflow: 'hidden', flex: 0 }, @@ -90,7 +91,7 @@ const _Canvas = forwardRef, CanvasPr layoutRef }) - const context2D = useContext2D(canvasRef.current) + const context2D = new CanvasRenderingContext2D(canvasRef.current) // 初始化bus和context2D useEffect(() => { @@ -104,16 +105,22 @@ const _Canvas = forwardRef, CanvasPr canvasRef.current.bus = new Bus(webviewPostMessage) canvasRef.current.bus.pause() - // 设置context2D + // 设置 context 2D canvasRef.current.context2D = context2D - // 设置getContext方法 + // 设置 getContext 方法 canvasRef.current.getContext = getContext - // 设置postMessage方法 + // 设置 createImage 方法 + canvasRef.current.createImage = createImage + + // 设置 postMessage 方法 canvasRef.current.postMessage = postMessage }, []) + const createImage = (width?: Number, height?: Number) => { + return canvasCreateImage(canvasRef.current, width, height) + } const getContext = useCallback((contextType: string) => { if (contextType === '2d') { return context2D diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx index b4509c6223..aabbc1cbcb 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -4,6 +4,8 @@ export const WEBVIEW_TARGET = '@@WEBVIEW_TARGET' export const constructors: Record = {} +export const ID = () => Math.random().toString(32).slice(2) + interface WebviewInstance { [WEBVIEW_TARGET]: string [key: string]: any @@ -27,11 +29,11 @@ interface WebviewMessage { type MessageListener = (message: WebviewMessage) => void -const setupWebviewTarget = (instance: WebviewInstance, targetName: string): void => { +export const setupWebviewTarget = (instance: WebviewInstance, targetName: string): void => { instance[WEBVIEW_TARGET] = targetName } -const setupWebviewProperties = (instance: WebviewInstance, properties: Record): void => { +export const setupWebviewProperties = (instance: WebviewInstance, properties: Record): void => { Object.entries(properties).forEach(([key, initialValue]) => { const privateKey = `__${key}__` instance[privateKey] = initialValue @@ -60,7 +62,7 @@ const setupWebviewProperties = (instance: WebviewInstance, properties: Record { +export const setupWebviewMethods = (instance: WebviewInstance, methods: string[]): void => { methods.forEach(method => { instance[method] = (...args: any[]) => { return instance.postMessage({ @@ -75,6 +77,31 @@ const setupWebviewMethods = (instance: WebviewInstance, methods: string[]): void }) } +export const setupWebviewConstructor = (instance: WebviewInstance, constructorName: string) => { + constructors[constructorName] = instance + instance.constructLocally = function (...args) { + // Pass noOnConstruction + return new instance(...args, true) + } + /** + * Arguments should be identical to the arguments passed to the constructor + * just without the canvas instance + */ + instance.prototype.onConstruction = function (...args) { + this[WEBVIEW_TARGET] = ID() + this.postMessage({ + type: 'construct', + payload: { + constructor: constructorName, + id: this[WEBVIEW_TARGET], + args + } + }) + } + instance.prototype.toJSON = function () { + return { __ref__: this[WEBVIEW_TARGET] } + } +} export const useWebviewBinding = ({ targetName, properties = {}, From c6ade88b79a687857c67e26fc5f7dab25a49f116 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Mon, 11 Nov 2024 21:02:13 +0800 Subject: [PATCH 09/26] =?UTF-8?q?chore:=20=E6=94=AF=E6=8C=81=E6=B8=90?= =?UTF-8?q?=E5=8F=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../react/mpx-canvas/CanvasGradient.ts | 22 ++++++--- .../mpx-canvas/CanvasRenderingContext2D.ts | 8 ++-- .../components/react/mpx-canvas/Image.ts | 45 +++++-------------- .../components/react/mpx-canvas/utils.tsx | 14 +++--- 4 files changed, 38 insertions(+), 51 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts index 6459b08c56..702c194414 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts @@ -1,15 +1,23 @@ -import { WEBVIEW_TARGET, constructors, setupWebviewMethods, setupWebviewConstructor } from './utils' -const METHODS = ['addColorStop'] +import { ID, WEBVIEW_TARGET, registerWebviewTarget, registerWebviewConstructor, registerWebviewMethods } from './utils' + export default class CanvasGradient { private canvas: any; - [WEBVIEW_TARGET]?: string; + [WEBVIEW_TARGET]: string; - constructor (canvas: any) { + constructor (canvas: any, noOnConstruction = false) { this.canvas = canvas - setupWebviewMethods(this, METHODS) - setupWebviewConstructor(this, 'CanvasGradient') + this[WEBVIEW_TARGET] = ID() + registerWebviewTarget(this, 'CanvasGradient') + registerWebviewMethods(this, ['addColorStop']) + if (!noOnConstruction) { + this.onConstruction() + } } - postMessage = (message: any) => { + + postMessage (message: any) { return this.canvas.postMessage(message) } } + +// 注册构造器 +registerWebviewConstructor(CanvasGradient, 'CanvasGradient') diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts index 0bf8efba2a..3f38645de4 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts @@ -1,4 +1,4 @@ -import { setupWebviewProperties, setupWebviewMethods, setupWebviewTarget } from './utils' +import { registerWebviewProperties, registerWebviewMethods, registerWebviewTarget } from './utils' const PROPERTIES = { fillStyle: '#000', @@ -60,9 +60,9 @@ export default class CanvasRenderingContext2D { canvas: any constructor(canvas) { this.canvas = canvas - setupWebviewTarget(this, 'context2D') - setupWebviewProperties(this, PROPERTIES) - setupWebviewMethods(this, METHODS) + registerWebviewTarget(this, 'context2D') + registerWebviewProperties(this, PROPERTIES) + registerWebviewMethods(this, METHODS) } postMessage(message) { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts index ff0c77569e..9d2f2b88bf 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts @@ -1,14 +1,18 @@ -import { WEBVIEW_TARGET, constructors, ID, setupWebviewProperties } from './utils' -import { warn } from '@mpxjs/utils' +import { registerWebviewProperties, registerWebviewConstructor } from './utils' + +const PROPERTIES = { + crossOrigin: undefined, + height: undefined, + src: undefined, + width: undefined +} export class Image { canvas: any; width: Number; height: Number; constructor (canvas: any, width: Number, height: Number, noOnConstruction?: Boolean) { - if (!(canvas instanceof Canvas)) { - warn('Image must be initialized with a Canvas instance') - } + registerWebviewProperties(this, PROPERTIES) this.canvas = canvas if (width) { this.width = width @@ -19,41 +23,16 @@ export class Image { if (!noOnConstruction) { this.onConstruction() } - setupWebviewProperties(this, { - crossOrigin: undefined, - height: undefined, - src: undefined, - width: undefined - }) } postMessage = (message: any) => { return this.canvas.postMessage(message) } - - // 添加静态方法用于本地构造实例 - static constructLocally (canvas: any, ...args: any[]) { - return new Image(canvas, ...args, true) - } - - onConstruction (...args) { - this[WEBVIEW_TARGET] = ID() - this.postMessage({ - type: 'construct', - payload: { - constructor: 'Image', - id: this[WEBVIEW_TARGET], - args - } - }) - } - - toJSON () { - return { __ref__: this[WEBVIEW_TARGET] } - } } export function createImage (canvas, height, width) { return new Image(canvas, height, width) } -constructors.Image = Image + +// 注册构造器 +registerWebviewConstructor(Image, 'Image') diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx index aabbc1cbcb..4d5a962377 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -29,11 +29,11 @@ interface WebviewMessage { type MessageListener = (message: WebviewMessage) => void -export const setupWebviewTarget = (instance: WebviewInstance, targetName: string): void => { +export const registerWebviewTarget = (instance: WebviewInstance, targetName: string): void => { instance[WEBVIEW_TARGET] = targetName } -export const setupWebviewProperties = (instance: WebviewInstance, properties: Record): void => { +export const registerWebviewProperties = (instance: WebviewInstance, properties: Record): void => { Object.entries(properties).forEach(([key, initialValue]) => { const privateKey = `__${key}__` instance[privateKey] = initialValue @@ -62,7 +62,7 @@ export const setupWebviewProperties = (instance: WebviewInstance, properties: Re }) } -export const setupWebviewMethods = (instance: WebviewInstance, methods: string[]): void => { +export const registerWebviewMethods = (instance: WebviewInstance, methods: string[]): void => { methods.forEach(method => { instance[method] = (...args: any[]) => { return instance.postMessage({ @@ -77,7 +77,7 @@ export const setupWebviewMethods = (instance: WebviewInstance, methods: string[] }) } -export const setupWebviewConstructor = (instance: WebviewInstance, constructorName: string) => { +export const registerWebviewConstructor = (instance: WebviewInstance, constructorName: string) => { constructors[constructorName] = instance instance.constructLocally = function (...args) { // Pass noOnConstruction @@ -116,9 +116,9 @@ export const useWebviewBinding = ({ useEffect(() => { if (instanceRef.current) { - setupWebviewTarget(instanceRef.current, targetName) - setupWebviewProperties(instanceRef.current, properties) - setupWebviewMethods(instanceRef.current, methods) + registerWebviewTarget(instanceRef.current, targetName) + registerWebviewProperties(instanceRef.current, properties) + registerWebviewMethods(instanceRef.current, methods) } }, [targetName, properties, methods]) From 9c510b4c52a19bc5a2b7db145eafecdf6bae4c41 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Tue, 12 Nov 2024 15:39:15 +0800 Subject: [PATCH 10/26] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81drawImage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../react/mpx-canvas/CanvasGradient.ts | 8 ++--- .../components/react/mpx-canvas/Image.ts | 17 ++++++----- .../components/react/mpx-canvas/index.html.ts | 30 +++++++++++++++++++ 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts index 702c194414..3a68bfa684 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts @@ -1,5 +1,6 @@ -import { ID, WEBVIEW_TARGET, registerWebviewTarget, registerWebviewConstructor, registerWebviewMethods } from './utils' +import { ID, WEBVIEW_TARGET, registerWebviewConstructor, registerWebviewMethods } from './utils' +const METHODS = ['addColorStop'] export default class CanvasGradient { private canvas: any; [WEBVIEW_TARGET]: string; @@ -7,8 +8,7 @@ export default class CanvasGradient { constructor (canvas: any, noOnConstruction = false) { this.canvas = canvas this[WEBVIEW_TARGET] = ID() - registerWebviewTarget(this, 'CanvasGradient') - registerWebviewMethods(this, ['addColorStop']) + registerWebviewMethods(this, METHODS) if (!noOnConstruction) { this.onConstruction() } @@ -19,5 +19,5 @@ export default class CanvasGradient { } } -// 注册构造器 +// 注册构造器, 需要通过 createLinearGradient 调用 registerWebviewConstructor(CanvasGradient, 'CanvasGradient') diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts index 9d2f2b88bf..d79bce60b9 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts @@ -9,29 +9,32 @@ const PROPERTIES = { export class Image { canvas: any; - width: Number; - height: Number; - constructor (canvas: any, width: Number, height: Number, noOnConstruction?: Boolean) { - registerWebviewProperties(this, PROPERTIES) + width: number; + height: number; + + constructor (canvas: any, width?: number, height?: number, noOnConstruction = false) { this.canvas = canvas + registerWebviewProperties(this, PROPERTIES) + if (width) { this.width = width } if (height) { this.height = height } + if (!noOnConstruction) { this.onConstruction() } } - postMessage = (message: any) => { + postMessage (message: any) { return this.canvas.postMessage(message) } } -export function createImage (canvas, height, width) { - return new Image(canvas, height, width) +export function createImage (canvas, width, height) { + return new Image(canvas, width, height) } // 注册构造器 diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts index 3c113e6961..a9c6d022bb 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts @@ -160,6 +160,7 @@ var toMessage = function (result) { result[WEBVIEW_TARGET] = id; targets[id] = result; } + return { type: "json", payload: flattenObject(result), @@ -186,6 +187,19 @@ var toArgs = function (result) { return args; }; +var createObjectsFromArgs = function (args) { + var _a; + for (var index = 0; index < args.length; index += 1) { + var currentArg = args[index]; + if (currentArg && currentArg.className !== undefined) { + var className = currentArg.className, classArgs = currentArg.classArgs; + var object = new ((_a = constructors[className]).bind.apply(_a, __spreadArray([void 0], classArgs, false)))(); + args[index] = object; + } + } + return args; +}; + var canvas = document.createElement("canvas"); var autoScaledCanvas = new AutoScaledCanvas(canvas); var targets = { @@ -231,6 +245,22 @@ function handleMessage(_a) { targets[target][key] = populateRefs(value); break; } + case "construct": { + var constructor = payload.constructor, target = payload.id, _d = payload.args, args = _d === void 0 ? [] : _d; + var newArgs = createObjectsFromArgs(args); + var object = void 0; + try { + object = new ((_c = constructors[constructor]).bind.apply(_c, __spreadArray([void 0], newArgs, false)))(); + } + catch (error) { + throw new Error("Error while constructing ".concat(constructor, " ").concat(error.message)); + } + object.__constructorName__ = constructor; + var message = toMessage({}); + targets[target] = object; + window.ReactNativeWebView.postMessage(JSON.stringify(__assign({ id: id }, message))); + break; + } } } var handleError = function (err, message) { From 1b56604704ea850c90a236ce8c91fb0591f42fbf Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Tue, 12 Nov 2024 19:05:05 +0800 Subject: [PATCH 11/26] =?UTF-8?q?feat:=20image=E6=94=AF=E6=8C=81load?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/react/mpx-canvas/Image.ts | 31 ++++++++++++++++++- .../components/react/mpx-canvas/index.html.ts | 21 +++++++++++++ .../components/react/mpx-canvas/index.tsx | 19 ++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts index d79bce60b9..3f8c4a742e 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts @@ -1,4 +1,4 @@ -import { registerWebviewProperties, registerWebviewConstructor } from './utils' +import { WEBVIEW_TARGET, registerWebviewProperties, registerWebviewConstructor } from './utils' const PROPERTIES = { crossOrigin: undefined, @@ -25,12 +25,41 @@ export class Image { if (!noOnConstruction) { this.onConstruction() + this.postMessage({ + type: 'listen', + payload: { + types: ['error', 'load'], + target: this[WEBVIEW_TARGET] + } + }) } } postMessage (message: any) { return this.canvas.postMessage(message) } + + addEventListener (type, callback) { + return this.canvas.addMessageListener((message) => { + if ( + message && + message.type === 'event' && + message.payload.target[WEBVIEW_TARGET] === this[WEBVIEW_TARGET] && + message.payload.type === type + ) { + for (const key in message.payload.target) { + const value = message.payload.target[key] + if (key in this && this[key] !== value) { + this[key] = value + } + } + callback({ + ...message.payload, + target: this + }) + } + }) + } } export function createImage (canvas, width, height) { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts index a9c6d022bb..a8a4307970 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts @@ -261,6 +261,27 @@ function handleMessage(_a) { window.ReactNativeWebView.postMessage(JSON.stringify(__assign({ id: id }, message))); break; } + case "listen": { + const { types, target } = payload; + for (const eventType of types) { + targets[target].addEventListener(eventType, (e) => { + const message = toMessage({ + type: "event", + payload: { + type: e.type, + target: { + ...flattenObject(targets[target]), + [WEBVIEW_TARGET]: target, + }, + }, + }); + window.ReactNativeWebView.postMessage( + JSON.stringify({ id, ...message }), + ); + }); + } + break; + } } } var handleError = function (err, message) { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index ac69192e05..f28d92a766 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -116,6 +116,13 @@ const _Canvas = forwardRef, CanvasPr // 设置 postMessage 方法 canvasRef.current.postMessage = postMessage + + // 设置 listeners + canvasRef.current.listeners = [] + + canvasRef.current.addMessageListener = addMessageListener + + canvasRef.current.removeMessageListener = removeMessageListener }, []) const createImage = (width?: Number, height?: Number) => { @@ -158,6 +165,15 @@ const _Canvas = forwardRef, CanvasPr } }, []) + const addMessageListener = (listener) => { + canvasRef.current.listeners.push(listener) + return () => canvasRef.current.removeMessageListener(listener) + } + + const removeMessageListener = (listener) => { + canvasRef.current.listeners.splice(canvasRef.current.listeners.indexOf(listener), 1) + } + const onMessage = useCallback((e) => { let data = JSON.parse(e.nativeEvent.data) switch (data.type) { @@ -190,6 +206,9 @@ const _Canvas = forwardRef, CanvasPr payload: object } } + for (const listener of canvasRef.current.listeners) { + listener(data.payload) + } } canvasRef.current.bus.handle(data) } From 1d6f7182a1de77b42c912a588f006e2498c720ba Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Tue, 12 Nov 2024 19:37:28 +0800 Subject: [PATCH 12/26] =?UTF-8?q?feat:=20img=E7=9B=B4=E6=8E=A5=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E8=B0=83=E7=94=A8onload?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/react/mpx-canvas/Image.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts index 3f8c4a742e..58b68b1902 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts @@ -11,6 +11,10 @@ export class Image { canvas: any; width: number; height: number; + private _loadListener: any; + private _errorListener: any; + private __onload: Function | undefined; + private _onerror: Function | undefined; constructor (canvas: any, width?: number, height?: number, noOnConstruction = false) { this.canvas = canvas @@ -60,6 +64,34 @@ export class Image { } }) } + + set onload (callback: Function | undefined) { + this.__onload = callback + if (this._loadListener) { + this.canvas.removeMessageListener(this._loadListener) + } + if (callback) { + this._loadListener = this.addEventListener('load', callback) + } + } + + get onload (): Function | undefined { + return this._onload + } + + set onerror (callback: Function | undefined) { + this._onerror = callback + if (this._errorListener) { + this.canvas.removeMessageListener(this._errorListener) + } + if (callback) { + this._errorListener = this.addEventListener('error', callback) + } + } + + get onerror (): Function | undefined { + return this._onerror + } } export function createImage (canvas, width, height) { From 272dbddef792f51f4929ed5cf1f5bb34f2a74a4a Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Tue, 12 Nov 2024 19:43:15 +0800 Subject: [PATCH 13/26] =?UTF-8?q?chore:=20=E5=88=A0=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=94=A8code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/mpx-canvas/CanvasGradient.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts index 3a68bfa684..e130ef6035 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts @@ -1,13 +1,11 @@ -import { ID, WEBVIEW_TARGET, registerWebviewConstructor, registerWebviewMethods } from './utils' +import { registerWebviewConstructor, registerWebviewMethods } from './utils' const METHODS = ['addColorStop'] export default class CanvasGradient { private canvas: any; - [WEBVIEW_TARGET]: string; constructor (canvas: any, noOnConstruction = false) { this.canvas = canvas - this[WEBVIEW_TARGET] = ID() registerWebviewMethods(this, METHODS) if (!noOnConstruction) { this.onConstruction() From 94149d66a8750f136a3a4ca1019cc8714cbd37b2 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Thu, 14 Nov 2024 16:30:33 +0800 Subject: [PATCH 14/26] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81createImage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/react/mpx-canvas/ImageData.ts | 23 +++++++++++++++++++ .../components/react/mpx-canvas/index.html.ts | 21 +++++++++++++++-- .../components/react/mpx-canvas/index.tsx | 6 +++++ .../components/react/mpx-canvas/utils.tsx | 16 +++++++++---- 4 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts new file mode 100644 index 0000000000..a2e3fc46be --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts @@ -0,0 +1,23 @@ +import { + registerWebviewConstructor +} from './utils' + +export default class ImageData { + constructor (canvas, dataArray, width, height, noOnConstruction) { + this.canvas = canvas + if (!noOnConstruction) { + this.onConstruction(dataArray, width, height) + } + } + + postMessage = (message) => { + return this.canvas.postMessage(message) + }; +} + +export function createImageData (canvas, dataArray, width, height) { + return new ImageData(canvas, dataArray, width, height) +} + +// 注册构造器 +registerWebviewConstructor(ImageData, 'ImageData') diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts index a8a4307970..9fb3816d6f 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts @@ -89,6 +89,10 @@ var flattenObjectCopyValue = function (flatObj, srcObj, key) { var flattenObject = function (object) { if (typeof object !== "object" || object === null) { return object; + } + // 处理 TypedArray + if (object instanceof Uint8ClampedArray) { + return Array.from(object); } var flatObject = {}; for (var key in object) { @@ -160,7 +164,6 @@ var toMessage = function (result) { result[WEBVIEW_TARGET] = id; targets[id] = result; } - return { type: "json", payload: flattenObject(result), @@ -193,6 +196,7 @@ var createObjectsFromArgs = function (args) { var currentArg = args[index]; if (currentArg && currentArg.className !== undefined) { var className = currentArg.className, classArgs = currentArg.classArgs; + // new ImageData,第一个参数需要是 Uint8ClampedArray var object = new ((_a = constructors[className]).bind.apply(_a, __spreadArray([void 0], classArgs, false)))(); args[index] = object; } @@ -208,13 +212,26 @@ var targets = { }; var constructors = { CanvasGradient: CanvasGradient, - Image: Image + Image: Image, + ImageData: ImageData, + Uint8ClampedArray: Uint8ClampedArray, }; + Image.bind = Image.bind || function () { return Image; }; +ImageData.bind = + ImageData.bind || + function () { + return ImageData; + }; +Uint8ClampedArray.bind = + Uint8ClampedArray.bind || + function () { + return Uint8ClampedArray; + }; var populateRefs = function (arg) { if (arg && arg.__ref__) { return targets[arg.__ref__]; diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index f28d92a766..30373665f4 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -25,6 +25,7 @@ import CanvasRenderingContext2D from './CanvasRenderingContext2D' import html from './index.html.ts' import './CanvasGradient' import { createImage as canvasCreateImage } from './Image' +import { createImageData as canvasCreateImageData } from './ImageData' const stylesheet = StyleSheet.create({ container: { overflow: 'hidden', flex: 0 }, @@ -123,8 +124,13 @@ const _Canvas = forwardRef, CanvasPr canvasRef.current.addMessageListener = addMessageListener canvasRef.current.removeMessageListener = removeMessageListener + + canvasRef.current.createImageData = createImageData }, []) + const createImageData = (dataArray, width?: Number, height?: Number) => { + return canvasCreateImageData(canvasRef.current, dataArray, width, height) + } const createImage = (width?: Number, height?: Number) => { return canvasCreateImage(canvasRef.current, width, height) } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx index 4d5a962377..630e2ff30e 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -6,6 +6,13 @@ export const constructors: Record = {} export const ID = () => Math.random().toString(32).slice(2) +const SPECIAL_CONSTRUCTOR = { + ImageData: { + className: 'Uint8ClampedArray', + paramNum: 0 + } +} + interface WebviewInstance { [WEBVIEW_TARGET]: string [key: string]: any @@ -83,11 +90,12 @@ export const registerWebviewConstructor = (instance: WebviewInstance, constructo // Pass noOnConstruction return new instance(...args, true) } - /** - * Arguments should be identical to the arguments passed to the constructor - * just without the canvas instance - */ + instance.prototype.onConstruction = function (...args) { + if (SPECIAL_CONSTRUCTOR[constructorName] !== undefined) { + const { className, paramNum } = SPECIAL_CONSTRUCTOR[constructorName] + args[paramNum] = { className, classArgs: [args[paramNum]] } + } this[WEBVIEW_TARGET] = ID() this.postMessage({ type: 'construct', From 2b4da541abbf5fa62936980f48aa94ef7917a8c5 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Thu, 14 Nov 2024 16:37:59 +0800 Subject: [PATCH 15/26] =?UTF-8?q?chore:=20=E5=88=A0=E9=99=A4=E5=BA=9F?= =?UTF-8?q?=E5=BC=83=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/mpx-canvas/OffscreenCanvas.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/OffscreenCanvas.ts diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/OffscreenCanvas.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/OffscreenCanvas.ts deleted file mode 100644 index e69de29bb2..0000000000 From 8c6c6d6ecd00cbd627231d9cafa2cda5fce8361b Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Thu, 14 Nov 2024 17:28:01 +0800 Subject: [PATCH 16/26] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81createImage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../react/mpx-canvas/CanvasGradient.ts | 2 +- .../mpx-canvas/CanvasRenderingContext2D.ts | 9 ++--- .../components/react/mpx-canvas/Image.ts | 8 ++-- .../{index.html.ts => index.html.js} | 0 .../components/react/mpx-canvas/index.tsx | 17 +++++---- .../components/react/mpx-canvas/utils.tsx | 37 ++++++++++--------- 6 files changed, 37 insertions(+), 36 deletions(-) rename packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/{index.html.ts => index.html.js} (100%) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts index e130ef6035..9582748549 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts @@ -7,7 +7,7 @@ export default class CanvasGradient { constructor (canvas: any, noOnConstruction = false) { this.canvas = canvas registerWebviewMethods(this, METHODS) - if (!noOnConstruction) { + if (this.onConstruction && !noOnConstruction) { this.onConstruction() } } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts index 3f38645de4..3470b75b7d 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts @@ -1,4 +1,4 @@ -import { registerWebviewProperties, registerWebviewMethods, registerWebviewTarget } from './utils' +import { WebviewMessage, registerWebviewProperties, registerWebviewMethods, registerWebviewTarget } from './utils' const PROPERTIES = { fillStyle: '#000', @@ -20,7 +20,6 @@ const PROPERTIES = { } const METHODS = [ - // draw、createCircularGradient、setFillStyle、 setFontSize、 setGlobalAlpha、 setLineCap、setLineJoin、setLineWidth、setMiterLimit、setShadow、setStrokeStyle、 setTextAlign、setTextBaseline 不支持 'arc', 'arcTo', 'beginPath', @@ -57,15 +56,15 @@ const METHODS = [ 'translate' ] export default class CanvasRenderingContext2D { - canvas: any - constructor(canvas) { + canvas: Record + constructor (canvas: Record) { this.canvas = canvas registerWebviewTarget(this, 'context2D') registerWebviewProperties(this, PROPERTIES) registerWebviewMethods(this, METHODS) } - postMessage(message) { + postMessage (message: WebviewMessage) { return this.canvas.postMessage(message) } } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts index 58b68b1902..4585576a23 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts @@ -1,4 +1,4 @@ -import { WEBVIEW_TARGET, registerWebviewProperties, registerWebviewConstructor } from './utils' +import { WebviewMessage, WEBVIEW_TARGET, registerWebviewProperties, registerWebviewConstructor } from './utils' const PROPERTIES = { crossOrigin: undefined, @@ -43,8 +43,8 @@ export class Image { return this.canvas.postMessage(message) } - addEventListener (type, callback) { - return this.canvas.addMessageListener((message) => { + addEventListener (type, callbackFn) { + return this.canvas.addMessageListener((message: WebviewMessage) => { if ( message && message.type === 'event' && @@ -57,7 +57,7 @@ export class Image { this[key] = value } } - callback({ + callbackFn({ ...message.payload, target: this }) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.js similarity index 100% rename from packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.ts rename to packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.js diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index 30373665f4..bd41e4e2b9 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -19,10 +19,11 @@ import Bus from './Bus' import { useWebviewBinding, constructors, - WEBVIEW_TARGET + WEBVIEW_TARGET, + WebviewMessage } from './utils' import CanvasRenderingContext2D from './CanvasRenderingContext2D' -import html from './index.html.ts' +import html from './index.html' import './CanvasGradient' import { createImage as canvasCreateImage } from './Image' import { createImageData as canvasCreateImageData } from './ImageData' @@ -96,7 +97,7 @@ const _Canvas = forwardRef, CanvasPr // 初始化bus和context2D useEffect(() => { - const webviewPostMessage = (message) => { + const webviewPostMessage = (message: WebviewMessage) => { if (canvasRef.current.webview) { canvasRef.current.webview.postMessage(JSON.stringify(message)) } @@ -128,7 +129,7 @@ const _Canvas = forwardRef, CanvasPr canvasRef.current.createImageData = createImageData }, []) - const createImageData = (dataArray, width?: Number, height?: Number) => { + const createImageData = (dataArray: Array, width?: Number, height?: Number) => { return canvasCreateImageData(canvasRef.current, dataArray, width, height) } const createImage = (width?: Number, height?: Number) => { @@ -141,7 +142,7 @@ const _Canvas = forwardRef, CanvasPr return null }, []) - const postMessage = useCallback(async (message) => { + const postMessage = useCallback(async (message: WebviewMessage) => { if (!canvasRef.current?.bus) return const { type, payload } = await canvasRef.current.bus.post({ id: Math.random(), @@ -171,16 +172,16 @@ const _Canvas = forwardRef, CanvasPr } }, []) - const addMessageListener = (listener) => { + const addMessageListener = (listener: any) => { canvasRef.current.listeners.push(listener) return () => canvasRef.current.removeMessageListener(listener) } - const removeMessageListener = (listener) => { + const removeMessageListener = (listener: any) => { canvasRef.current.listeners.splice(canvasRef.current.listeners.indexOf(listener), 1) } - const onMessage = useCallback((e) => { + const onMessage = useCallback((e: { nativeEvent: { data: string } }) => { let data = JSON.parse(e.nativeEvent.data) switch (data.type) { case 'error': { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx index 630e2ff30e..e3b1d06bc5 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -6,22 +6,24 @@ export const constructors: Record = {} export const ID = () => Math.random().toString(32).slice(2) -const SPECIAL_CONSTRUCTOR = { +const SPECIAL_CONSTRUCTOR: Record = { ImageData: { className: 'Uint8ClampedArray', paramNum: 0 } } -interface WebviewInstance { - [WEBVIEW_TARGET]: string - [key: string]: any +interface Instance { postMessage: (message: WebviewMessage) => void + addMessageListener?: (listener: MessageListener) => void + onConstruction?:(...args: any[]) => void + constructLocally?:(...args: unknown[]) => void forceUpdate?: () => void - addMessageListener: (listener: MessageListener) => void + [WEBVIEW_TARGET]?: string + [key: string]: any } -interface WebviewMessage { +export interface WebviewMessage { type: 'set' | 'exec' | 'listen' | 'event' payload: { target?: string | { [WEBVIEW_TARGET]: string, [key: string]: any } @@ -36,11 +38,11 @@ interface WebviewMessage { type MessageListener = (message: WebviewMessage) => void -export const registerWebviewTarget = (instance: WebviewInstance, targetName: string): void => { +export const registerWebviewTarget = (instance: Instance, targetName: string): void => { instance[WEBVIEW_TARGET] = targetName } -export const registerWebviewProperties = (instance: WebviewInstance, properties: Record): void => { +export const registerWebviewProperties = (instance: Instance, properties: Record): void => { Object.entries(properties).forEach(([key, initialValue]) => { const privateKey = `__${key}__` instance[privateKey] = initialValue @@ -69,7 +71,7 @@ export const registerWebviewProperties = (instance: WebviewInstance, properties: }) } -export const registerWebviewMethods = (instance: WebviewInstance, methods: string[]): void => { +export const registerWebviewMethods = (instance: Instance, methods: string[]): void => { methods.forEach(method => { instance[method] = (...args: any[]) => { return instance.postMessage({ @@ -84,14 +86,13 @@ export const registerWebviewMethods = (instance: WebviewInstance, methods: strin }) } -export const registerWebviewConstructor = (instance: WebviewInstance, constructorName: string) => { +export const registerWebviewConstructor = (instance: Instance, constructorName: string): void => { constructors[constructorName] = instance - instance.constructLocally = function (...args) { - // Pass noOnConstruction - return new instance(...args, true) + instance.constructLocally = function (...args: unknown[]): Instance { + return new (instance as any)(...args, true) } - instance.prototype.onConstruction = function (...args) { + instance.constructor.prototype.onConstruction = function (...args: any[]): void { if (SPECIAL_CONSTRUCTOR[constructorName] !== undefined) { const { className, paramNum } = SPECIAL_CONSTRUCTOR[constructorName] args[paramNum] = { className, classArgs: [args[paramNum]] } @@ -120,13 +121,13 @@ export const useWebviewBinding = ({ methods?: string[]; constructorName?: string }) => { - const instanceRef = useRef({}) + const instanceRef = useRef({}) useEffect(() => { if (instanceRef.current) { - registerWebviewTarget(instanceRef.current, targetName) - registerWebviewProperties(instanceRef.current, properties) - registerWebviewMethods(instanceRef.current, methods) + registerWebviewTarget(instanceRef.current as Instance, targetName) + registerWebviewProperties(instanceRef.current as Instance, properties) + registerWebviewMethods(instanceRef.current as Instance, methods) } }, [targetName, properties, methods]) From 658ce50f9f2e6d0665458595bf209c972213de90 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Thu, 14 Nov 2024 20:32:45 +0800 Subject: [PATCH 17/26] =?UTF-8?q?chore:=20=E6=94=AF=E6=8C=81context?= =?UTF-8?q?=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/mpx-canvas/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index bd41e4e2b9..c1d4a858e6 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -230,7 +230,8 @@ const _Canvas = forwardRef, CanvasPr }, []) useNodesRef(props, ref, nodeRef, { - node: canvasRef.current + node: canvasRef.current, + context: context2D }) if (Platform.OS === 'android') { From 136f18ce5d94af323b53e26b6bcc6a8f3e85cfa1 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Fri, 15 Nov 2024 11:00:58 +0800 Subject: [PATCH 18/26] fix: error --- .../lib/runtime/components/react/mpx-canvas/Image.ts | 2 +- .../lib/runtime/components/react/mpx-canvas/ImageData.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts index 4585576a23..b3fc298ecd 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts @@ -27,7 +27,7 @@ export class Image { this.height = height } - if (!noOnConstruction) { + if (this.onConstruction && !noOnConstruction) { this.onConstruction() this.postMessage({ type: 'listen', diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts index a2e3fc46be..c442e7934d 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts @@ -5,7 +5,7 @@ import { export default class ImageData { constructor (canvas, dataArray, width, height, noOnConstruction) { this.canvas = canvas - if (!noOnConstruction) { + if (this.onConstruction && !noOnConstruction) { this.onConstruction(dataArray, width, height) } } From b680136fd518dbe28ecaa47af1743b35a82b3cbf Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Fri, 15 Nov 2024 11:24:30 +0800 Subject: [PATCH 19/26] fix: error --- .../components/react/mpx-canvas/{index.html.js => html.ts} | 0 .../lib/runtime/components/react/mpx-canvas/index.tsx | 2 +- .../lib/runtime/components/react/mpx-canvas/utils.tsx | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/{index.html.js => html.ts} (100%) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.js b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/html.ts similarity index 100% rename from packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.html.js rename to packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/html.ts diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index c1d4a858e6..6bf9a56b0d 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -23,7 +23,7 @@ import { WebviewMessage } from './utils' import CanvasRenderingContext2D from './CanvasRenderingContext2D' -import html from './index.html' +import html from './html' import './CanvasGradient' import { createImage as canvasCreateImage } from './Image' import { createImageData as canvasCreateImageData } from './ImageData' diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx index e3b1d06bc5..883b906cc1 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -92,7 +92,7 @@ export const registerWebviewConstructor = (instance: Instance, constructorName: return new (instance as any)(...args, true) } - instance.constructor.prototype.onConstruction = function (...args: any[]): void { + instance.prototype.onConstruction = function (...args: any[]): void { if (SPECIAL_CONSTRUCTOR[constructorName] !== undefined) { const { className, paramNum } = SPECIAL_CONSTRUCTOR[constructorName] args[paramNum] = { className, classArgs: [args[paramNum]] } From f29bb5a7a849dcd8221f9b693f7ddffaa7e0f1a3 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Fri, 15 Nov 2024 14:20:20 +0800 Subject: [PATCH 20/26] =?UTF-8?q?chore:=20=E8=A1=A5=E5=85=85context2d=20ME?= =?UTF-8?q?THODS=20&=20PROPERTIES?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mpx-canvas/CanvasRenderingContext2D.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts index 3470b75b7d..8f70f63eab 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts @@ -1,10 +1,18 @@ import { WebviewMessage, registerWebviewProperties, registerWebviewMethods, registerWebviewTarget } from './utils' const PROPERTIES = { + direction: 'inherit', fillStyle: '#000', + filter: 'none', font: '10px sans-serif', + fontKerning: 'auto', + fontStretch: 'auto', + fontVariantCaps: 'normal', globalAlpha: 1.0, globalCompositeOperation: 'source-over', + imageSmoothingEnabled: 'true', + imageSmoothingQuality: 'low', + letterSpacing: '0px', lineCap: 'butt', lineDashOffset: 0.0, lineJoin: 'miter', @@ -16,7 +24,9 @@ const PROPERTIES = { shadowOffsetY: 0, strokeStyle: '#000', textAlign: 'start', - textBaseline: 'alphabetic' + textBaseline: 'alphabetic', + textRendering: 'auto', + wordSpacing: '0px' } const METHODS = [ @@ -27,24 +37,31 @@ const METHODS = [ 'clearRect', 'clip', 'closePath', + 'createConicGradient', 'createImageData', 'createLinearGradient', 'createPattern', 'createRadialGradient', + 'drawFocusIfNeeded', 'drawImage', + 'ellipse', 'fill', 'fillRect', 'fillText', 'getImageData', 'getLineDash', + 'getTransform', 'lineTo', 'measureText', 'moveTo', 'putImageData', 'quadraticCurveTo', 'rect', + 'reset', + 'resetTransform', 'restore', 'rotate', + 'roundRect', 'save', 'scale', 'setLineDash', From 55bdc6004cee99e0c3667341ad9ebd81a524bf74 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Fri, 15 Nov 2024 17:16:47 +0800 Subject: [PATCH 21/26] save --- .../react/mpx-canvas/CanvasGradient.ts | 6 ++-- .../mpx-canvas/CanvasRenderingContext2D.ts | 6 ++-- .../components/react/mpx-canvas/Image.ts | 29 ++++++++------- .../components/react/mpx-canvas/index.tsx | 35 +++++++++++-------- .../components/react/mpx-canvas/utils.tsx | 34 ++++++++++++------ .../lib/runtime/components/react/utils.tsx | 4 +++ 6 files changed, 70 insertions(+), 44 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts index 9582748549..db616ffaa5 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts @@ -1,10 +1,10 @@ -import { registerWebviewConstructor, registerWebviewMethods } from './utils' +import { CanvasInstance, registerWebviewConstructor, registerWebviewMethods } from './utils' const METHODS = ['addColorStop'] export default class CanvasGradient { - private canvas: any; + private canvas: CanvasInstance; - constructor (canvas: any, noOnConstruction = false) { + constructor (canvas: CanvasInstance, noOnConstruction = false) { this.canvas = canvas registerWebviewMethods(this, METHODS) if (this.onConstruction && !noOnConstruction) { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts index 8f70f63eab..0726e84a07 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasRenderingContext2D.ts @@ -1,4 +1,4 @@ -import { WebviewMessage, registerWebviewProperties, registerWebviewMethods, registerWebviewTarget } from './utils' +import { CanvasInstance, WebviewMessage, registerWebviewProperties, registerWebviewMethods, registerWebviewTarget } from './utils' const PROPERTIES = { direction: 'inherit', @@ -73,8 +73,8 @@ const METHODS = [ 'translate' ] export default class CanvasRenderingContext2D { - canvas: Record - constructor (canvas: Record) { + canvas: CanvasInstance + constructor (canvas: CanvasInstance) { this.canvas = canvas registerWebviewTarget(this, 'context2D') registerWebviewProperties(this, PROPERTIES) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts index b3fc298ecd..3538b9e7ff 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts @@ -1,4 +1,5 @@ -import { WebviewMessage, WEBVIEW_TARGET, registerWebviewProperties, registerWebviewConstructor } from './utils' +import { WebviewMessage, WEBVIEW_TARGET, registerWebviewProperties, registerWebviewConstructor, CanvasInstance } from './utils' +import { MutableRefObject } from 'react' const PROPERTIES = { crossOrigin: undefined, @@ -8,15 +9,18 @@ const PROPERTIES = { } export class Image { + [WEBVIEW_TARGET]: string; canvas: any; width: number; height: number; private _loadListener: any; private _errorListener: any; - private __onload: Function | undefined; - private _onerror: Function | undefined; + private _onload: ((...args: any[]) => void); + private _onerror: ((...args: any[]) => void); + onConstruction?: ((...args: any[]) => void); + [key: string]: any; - constructor (canvas: any, width?: number, height?: number, noOnConstruction = false) { + constructor (canvas: CanvasInstance, width?: number, height?: number, noOnConstruction = false) { this.canvas = canvas registerWebviewProperties(this, PROPERTIES) @@ -43,16 +47,17 @@ export class Image { return this.canvas.postMessage(message) } - addEventListener (type, callbackFn) { + addEventListener (type: 'load' | 'error', callbackFn: Function) { return this.canvas.addMessageListener((message: WebviewMessage) => { + const target = message.payload.target as { [key: string]: any } || {} if ( message && message.type === 'event' && - message.payload.target[WEBVIEW_TARGET] === this[WEBVIEW_TARGET] && + target[WEBVIEW_TARGET] === this[WEBVIEW_TARGET] && message.payload.type === type ) { - for (const key in message.payload.target) { - const value = message.payload.target[key] + for (const key in target) { + const value = target[key] if (key in this && this[key] !== value) { this[key] = value } @@ -65,8 +70,8 @@ export class Image { }) } - set onload (callback: Function | undefined) { - this.__onload = callback + set onload (callback: ((...args: any[]) => void)) { + this._onload = callback if (this._loadListener) { this.canvas.removeMessageListener(this._loadListener) } @@ -79,7 +84,7 @@ export class Image { return this._onload } - set onerror (callback: Function | undefined) { + set onerror (callback: ((...args: any[]) => void)) { this._onerror = callback if (this._errorListener) { this.canvas.removeMessageListener(this._errorListener) @@ -94,7 +99,7 @@ export class Image { } } -export function createImage (canvas, width, height) { +export function createImage (canvas: CanvasInstance, width?: number, height?: number) { return new Image(canvas, width, height) } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index 6bf9a56b0d..fa200ea520 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -9,18 +9,20 @@ * ✔ bindlongtap * ✔ binderror */ -import React, { useRef, useState, useCallback, useEffect, forwardRef, JSX, TouchEvent } from 'react' +import React, { useRef, useState, useCallback, useEffect, forwardRef, JSX, TouchEvent, MutableRefObject } from 'react' import { View, Platform, StyleSheet, NativeSyntheticEvent } from 'react-native' import { WebView } from 'react-native-webview' import useNodesRef, { HandlerRef } from '../useNodesRef' -import { useLayout, useTransformStyle } from '../utils' +import { useLayout, useTransformStyle, extendObject } from '../utils' import useInnerProps, { getCustomEvent } from '../getInnerListeners' import Bus from './Bus' import { useWebviewBinding, constructors, WEBVIEW_TARGET, - WebviewMessage + WebviewMessage, + ID, + CanvasInstance } from './utils' import CanvasRenderingContext2D from './CanvasRenderingContext2D' import html from './html' @@ -61,22 +63,15 @@ interface CanvasProps { const _Canvas = forwardRef, CanvasProps>((props: CanvasProps = {}, ref): JSX.Element => { const { style = {}, originWhitelist = ['*'], 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props - const { width, height } = style const [isLoaded, setIsLoaded] = useState(false) const nodeRef = useRef(null) - const canvasRef = useWebviewBinding({ - targetName: 'canvas', - properties: { width, height }, - methods: ['toDataURL'] - }) - const { normalStyle, hasSelfPercent, setWidth, setHeight - } = useTransformStyle(style, { + } = useTransformStyle(extendObject(style, stylesheet.container), { enableVar, externalVarContext, parentFontSize, @@ -84,10 +79,17 @@ const _Canvas = forwardRef, CanvasPr parentHeight }) + const { width, height } = normalStyle + const canvasRef = useWebviewBinding({ + targetName: 'canvas', + properties: { width, height }, + methods: ['toDataURL'] + }) as MutableRefObject + const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef }) const innerProps = useInnerProps(props, { ref: nodeRef, - style: { ...normalStyle, ...layoutStyle, ...stylesheet.container, ...{ width, height, opacity: isLoaded ? 1 : 0 }, ...style }, + style: extendObject(normalStyle, layoutStyle, { opacity: isLoaded ? 1 : 0 }), ...layoutProps }, [], { layoutRef @@ -132,7 +134,7 @@ const _Canvas = forwardRef, CanvasPr const createImageData = (dataArray: Array, width?: Number, height?: Number) => { return canvasCreateImageData(canvasRef.current, dataArray, width, height) } - const createImage = (width?: Number, height?: Number) => { + const createImage = (width?: number, height?: number) => { return canvasCreateImage(canvasRef.current, width, height) } const getContext = useCallback((contextType: string) => { @@ -145,7 +147,7 @@ const _Canvas = forwardRef, CanvasPr const postMessage = useCallback(async (message: WebviewMessage) => { if (!canvasRef.current?.bus) return const { type, payload } = await canvasRef.current.bus.post({ - id: Math.random(), + id: ID(), ...message }) @@ -217,7 +219,9 @@ const _Canvas = forwardRef, CanvasPr listener(data.payload) } } - canvasRef.current.bus.handle(data) + if (canvasRef.current.bus) { + canvasRef.current.bus.handle(data) + } } } }, []) @@ -230,6 +234,7 @@ const _Canvas = forwardRef, CanvasPr }, []) useNodesRef(props, ref, nodeRef, { + style: normalStyle, node: canvasRef.current, context: context2D }) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx index 883b906cc1..1c09beca3a 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -1,4 +1,6 @@ import { useEffect, useRef } from 'react' +import { WebView } from 'react-native-webview' +import Bus from './Bus' export const WEBVIEW_TARGET = '@@WEBVIEW_TARGET' @@ -12,21 +14,19 @@ const SPECIAL_CONSTRUCTOR: Record void - addMessageListener?: (listener: MessageListener) => void - onConstruction?:(...args: any[]) => void - constructLocally?:(...args: unknown[]) => void - forceUpdate?: () => void - [WEBVIEW_TARGET]?: string - [key: string]: any + new (...args: any[]): { + postMessage: (message: any) => void; + [key: string]: any; + } + postMessage: (message: WebviewMessage) => void; + [key: string]: any; } export interface WebviewMessage { type: 'set' | 'exec' | 'listen' | 'event' payload: { - target?: string | { [WEBVIEW_TARGET]: string, [key: string]: any } + target?: string | { [key: string]: any } key?: string value?: any method?: string @@ -36,6 +36,19 @@ export interface WebviewMessage { } } +export interface CanvasInstance { + webview: WebView | null; + bus: Bus | null; + context2D: CanvasRenderingContext2D; + getContext: (contextType: string) => CanvasRenderingContext2D | null; + createImage: (width?: number, height?: number) => any; + postMessage: (message: WebviewMessage) => Promise; + listeners: Array<(payload: any) => void>; + addMessageListener: (listener: (payload: any) => void) => () => void; + removeMessageListener: (listener: (payload: any) => void) => void; + createImageData: (dataArray: number[], width?: number, height?: number) => any; +} + type MessageListener = (message: WebviewMessage) => void export const registerWebviewTarget = (instance: Instance, targetName: string): void => { @@ -118,8 +131,7 @@ export const useWebviewBinding = ({ }: { targetName: string; properties?: Record; - methods?: string[]; - constructorName?: string + methods?: string[] }) => { const instanceRef = useRef({}) diff --git a/packages/webpack-plugin/lib/runtime/components/react/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/utils.tsx index 790941dc32..7fd05d37d2 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/utils.tsx @@ -524,3 +524,7 @@ export function wrapChildren (props: Record = {}, { hasVarDec, varC } return children } + +export function extendObject (...args: Record[]) { + return Object.assign({}, ...args) +} From 7cf4e711c4b4f77c98f893ea029c64f8bd309b6d Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Fri, 15 Nov 2024 18:36:10 +0800 Subject: [PATCH 22/26] fix: error --- .../lib/runtime/components/react/mpx-canvas/Image.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts index 3538b9e7ff..282adaa25a 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts @@ -1,5 +1,4 @@ import { WebviewMessage, WEBVIEW_TARGET, registerWebviewProperties, registerWebviewConstructor, CanvasInstance } from './utils' -import { MutableRefObject } from 'react' const PROPERTIES = { crossOrigin: undefined, @@ -17,7 +16,6 @@ export class Image { private _errorListener: any; private _onload: ((...args: any[]) => void); private _onerror: ((...args: any[]) => void); - onConstruction?: ((...args: any[]) => void); [key: string]: any; constructor (canvas: CanvasInstance, width?: number, height?: number, noOnConstruction = false) { @@ -49,7 +47,7 @@ export class Image { addEventListener (type: 'load' | 'error', callbackFn: Function) { return this.canvas.addMessageListener((message: WebviewMessage) => { - const target = message.payload.target as { [key: string]: any } || {} + const target = message?.payload?.target as { [key: string]: any } || {} if ( message && message.type === 'event' && From 745a6cb211153d5c89a884428766354936e84a1d Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Fri, 15 Nov 2024 18:48:49 +0800 Subject: [PATCH 23/26] fix: tsc error --- .../runtime/components/react/mpx-canvas/ImageData.ts | 11 +++++++---- .../lib/runtime/components/react/mpx-canvas/index.tsx | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts index c442e7934d..7ee5db766b 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts @@ -1,21 +1,24 @@ import { - registerWebviewConstructor + WebviewMessage, + registerWebviewConstructor, + CanvasInstance } from './utils' export default class ImageData { - constructor (canvas, dataArray, width, height, noOnConstruction) { + canvas: CanvasInstance; + constructor (canvas: CanvasInstance, dataArray: number[], width: number, height: number, noOnConstruction?: boolean) { this.canvas = canvas if (this.onConstruction && !noOnConstruction) { this.onConstruction(dataArray, width, height) } } - postMessage = (message) => { + postMessage = (message: WebviewMessage) => { return this.canvas.postMessage(message) }; } -export function createImageData (canvas, dataArray, width, height) { +export function createImageData (canvas: CanvasInstance, dataArray: number[], width: number, height: number) { return new ImageData(canvas, dataArray, width, height) } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index fa200ea520..55110e6ba3 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -131,7 +131,7 @@ const _Canvas = forwardRef, CanvasPr canvasRef.current.createImageData = createImageData }, []) - const createImageData = (dataArray: Array, width?: Number, height?: Number) => { + const createImageData = (dataArray: Array, width: number, height: number) => { return canvasCreateImageData(canvasRef.current, dataArray, width, height) } const createImage = (width?: number, height?: number) => { From 6b4b2baf04f1dc81ee822161dd7cbef06094ba6b Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Mon, 18 Nov 2024 20:09:35 +0800 Subject: [PATCH 24/26] fix: ts error --- .../react/mpx-canvas/CanvasGradient.ts | 9 ++--- .../components/react/mpx-canvas/Image.ts | 11 +++---- .../components/react/mpx-canvas/ImageData.ts | 5 +-- .../react/mpx-canvas/constructorsRegistry.ts | 33 +++++++++++++++++++ .../components/react/mpx-canvas/index.tsx | 8 +++-- .../components/react/mpx-canvas/utils.tsx | 33 ++++++++++--------- 6 files changed, 65 insertions(+), 34 deletions(-) create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/constructorsRegistry.ts diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts index db616ffaa5..daec5b69de 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/CanvasGradient.ts @@ -1,9 +1,9 @@ -import { CanvasInstance, registerWebviewConstructor, registerWebviewMethods } from './utils' +import { WebviewMessage, CanvasInstance, registerWebviewMethods } from './utils' const METHODS = ['addColorStop'] export default class CanvasGradient { private canvas: CanvasInstance; - + [key: string]: any; constructor (canvas: CanvasInstance, noOnConstruction = false) { this.canvas = canvas registerWebviewMethods(this, METHODS) @@ -12,10 +12,7 @@ export default class CanvasGradient { } } - postMessage (message: any) { + postMessage (message: WebviewMessage) { return this.canvas.postMessage(message) } } - -// 注册构造器, 需要通过 createLinearGradient 调用 -registerWebviewConstructor(CanvasGradient, 'CanvasGradient') diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts index 282adaa25a..fcb6368479 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/Image.ts @@ -1,4 +1,4 @@ -import { WebviewMessage, WEBVIEW_TARGET, registerWebviewProperties, registerWebviewConstructor, CanvasInstance } from './utils' +import { WebviewMessage, WEBVIEW_TARGET, registerWebviewProperties, CanvasInstance } from './utils' const PROPERTIES = { crossOrigin: undefined, @@ -41,7 +41,7 @@ export class Image { } } - postMessage (message: any) { + postMessage (message: WebviewMessage) { return this.canvas.postMessage(message) } @@ -78,7 +78,7 @@ export class Image { } } - get onload (): Function | undefined { + get onload (): ((...args: any[]) => void) { return this._onload } @@ -92,7 +92,7 @@ export class Image { } } - get onerror (): Function | undefined { + get onerror () : ((...args: any[]) => void) { return this._onerror } } @@ -100,6 +100,3 @@ export class Image { export function createImage (canvas: CanvasInstance, width?: number, height?: number) { return new Image(canvas, width, height) } - -// 注册构造器 -registerWebviewConstructor(Image, 'Image') diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts index 7ee5db766b..c5819be859 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/ImageData.ts @@ -1,11 +1,11 @@ import { WebviewMessage, - registerWebviewConstructor, CanvasInstance } from './utils' export default class ImageData { canvas: CanvasInstance; + [key: string]: any; constructor (canvas: CanvasInstance, dataArray: number[], width: number, height: number, noOnConstruction?: boolean) { this.canvas = canvas if (this.onConstruction && !noOnConstruction) { @@ -21,6 +21,3 @@ export default class ImageData { export function createImageData (canvas: CanvasInstance, dataArray: number[], width: number, height: number) { return new ImageData(canvas, dataArray, width, height) } - -// 注册构造器 -registerWebviewConstructor(ImageData, 'ImageData') diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/constructorsRegistry.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/constructorsRegistry.ts new file mode 100644 index 0000000000..0d79c1324f --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/constructorsRegistry.ts @@ -0,0 +1,33 @@ +import { Image } from './Image' +import CanvasGradient from './CanvasGradient' +import ImageData from './ImageData' +import { Instance, WebviewConstructor } from './utils' + +export enum ConstructorType { + Image = 'Image', + CanvasGradient = 'CanvasGradient', + ImageData = 'ImageData' +} + +interface Constructor { + type: ConstructorType + instance: new (...args: any[]) => Instance +} + +export class ConstructorsRegistry { + private static constructors: Constructor[] = [ + { type: ConstructorType.Image, instance: Image }, + { type: ConstructorType.CanvasGradient, instance: CanvasGradient }, + { type: ConstructorType.ImageData, instance: ImageData } + ] + + static register (registerWebviewConstructor: Function): void { + this.constructors.forEach(({ type, instance }) => { + registerWebviewConstructor(instance, type) + }) + } + + static get (type: ConstructorType): WebviewConstructor | undefined { + return this.constructors.find(c => c.type === type)?.instance + } +} diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index 55110e6ba3..7547f11155 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -22,13 +22,15 @@ import { WEBVIEW_TARGET, WebviewMessage, ID, - CanvasInstance + CanvasInstance, + registerWebviewConstructor } from './utils' import CanvasRenderingContext2D from './CanvasRenderingContext2D' import html from './html' import './CanvasGradient' import { createImage as canvasCreateImage } from './Image' import { createImageData as canvasCreateImageData } from './ImageData' +import { ConstructorsRegistry } from './constructorsRegistry' const stylesheet = StyleSheet.create({ container: { overflow: 'hidden', flex: 0 }, @@ -95,10 +97,12 @@ const _Canvas = forwardRef, CanvasPr layoutRef }) - const context2D = new CanvasRenderingContext2D(canvasRef.current) + const context2D = new CanvasRenderingContext2D(canvasRef.current) as any // 初始化bus和context2D useEffect(() => { + ConstructorsRegistry.register(registerWebviewConstructor) + const webviewPostMessage = (message: WebviewMessage) => { if (canvasRef.current.webview) { canvasRef.current.webview.postMessage(JSON.stringify(message)) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx index 1c09beca3a..6675f0621c 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -14,17 +14,20 @@ const SPECIAL_CONSTRUCTOR: Record void; - [key: string]: any; - } - postMessage: (message: WebviewMessage) => void; + +export interface Instance { + postMessage: (...args: any[]) => void; + [WEBVIEW_TARGET]?: string; [key: string]: any; } +export interface WebviewConstructor { + new (...args: any[]): Instance; + constructLocally?: (...args: unknown[]) => Instance; +} + export interface WebviewMessage { - type: 'set' | 'exec' | 'listen' | 'event' + type: 'set' | 'exec' | 'listen' | 'event' | 'construct' payload: { target?: string | { [key: string]: any } key?: string @@ -33,6 +36,8 @@ export interface WebviewMessage { args?: any[] types?: string[] type?: string + constructor?: string | Function + id?: string } } @@ -49,8 +54,6 @@ export interface CanvasInstance { createImageData: (dataArray: number[], width?: number, height?: number) => any; } -type MessageListener = (message: WebviewMessage) => void - export const registerWebviewTarget = (instance: Instance, targetName: string): void => { instance[WEBVIEW_TARGET] = targetName } @@ -99,13 +102,13 @@ export const registerWebviewMethods = (instance: Instance, methods: string[]): v }) } -export const registerWebviewConstructor = (instance: Instance, constructorName: string): void => { - constructors[constructorName] = instance - instance.constructLocally = function (...args: unknown[]): Instance { - return new (instance as any)(...args, true) +export const registerWebviewConstructor = (constructor: WebviewConstructor, constructorName: string): void => { + constructors[constructorName] = constructor + constructor.constructLocally = function (...args: unknown[]): Instance { + return new (constructor as any)(...args, true) } - instance.prototype.onConstruction = function (...args: any[]): void { + constructor.prototype.onConstruction = function (...args: any[]): void { if (SPECIAL_CONSTRUCTOR[constructorName] !== undefined) { const { className, paramNum } = SPECIAL_CONSTRUCTOR[constructorName] args[paramNum] = { className, classArgs: [args[paramNum]] } @@ -120,7 +123,7 @@ export const registerWebviewConstructor = (instance: Instance, constructorName: } }) } - instance.prototype.toJSON = function () { + constructor.prototype.toJSON = function () { return { __ref__: this[WEBVIEW_TARGET] } } } From 0fa50425d21ae7a4951f9de4a7c0b49b4c447b31 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Mon, 18 Nov 2024 20:23:30 +0800 Subject: [PATCH 25/26] =?UTF-8?q?chore:=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../react/mpx-canvas/constructorsRegistry.ts | 29 +++++++++++-------- .../components/react/mpx-canvas/index.tsx | 7 +++-- .../components/react/mpx-canvas/utils.tsx | 2 +- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/constructorsRegistry.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/constructorsRegistry.ts index 0d79c1324f..25b6731e1b 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/constructorsRegistry.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/constructorsRegistry.ts @@ -1,7 +1,7 @@ import { Image } from './Image' import CanvasGradient from './CanvasGradient' import ImageData from './ImageData' -import { Instance, WebviewConstructor } from './utils' +import { WebviewConstructor } from './utils' export enum ConstructorType { Image = 'Image', @@ -11,23 +11,28 @@ export enum ConstructorType { interface Constructor { type: ConstructorType - instance: new (...args: any[]) => Instance + instance: WebviewConstructor } -export class ConstructorsRegistry { - private static constructors: Constructor[] = [ - { type: ConstructorType.Image, instance: Image }, - { type: ConstructorType.CanvasGradient, instance: CanvasGradient }, - { type: ConstructorType.ImageData, instance: ImageData } - ] +const constructors: Constructor[] = [ + { type: ConstructorType.Image, instance: Image }, + { type: ConstructorType.CanvasGradient, instance: CanvasGradient }, + { type: ConstructorType.ImageData, instance: ImageData } +] - static register (registerWebviewConstructor: Function): void { - this.constructors.forEach(({ type, instance }) => { +export function useConstructorsRegistry () { + const register = (registerWebviewConstructor: Function): void => { + constructors.forEach(({ type, instance }) => { registerWebviewConstructor(instance, type) }) } - static get (type: ConstructorType): WebviewConstructor | undefined { - return this.constructors.find(c => c.type === type)?.instance + const getConstructor = (type: ConstructorType): WebviewConstructor | undefined => { + return constructors.find(c => c.type === type)?.instance + } + + return { + register, + getConstructor } } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx index 7547f11155..afd1bf243d 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/index.tsx @@ -30,7 +30,7 @@ import html from './html' import './CanvasGradient' import { createImage as canvasCreateImage } from './Image' import { createImageData as canvasCreateImageData } from './ImageData' -import { ConstructorsRegistry } from './constructorsRegistry' +import { useConstructorsRegistry } from './constructorsRegistry' const stylesheet = StyleSheet.create({ container: { overflow: 'hidden', flex: 0 }, @@ -88,6 +88,8 @@ const _Canvas = forwardRef, CanvasPr methods: ['toDataURL'] }) as MutableRefObject + const { register } = useConstructorsRegistry() + const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef }) const innerProps = useInnerProps(props, { ref: nodeRef, @@ -99,10 +101,9 @@ const _Canvas = forwardRef, CanvasPr const context2D = new CanvasRenderingContext2D(canvasRef.current) as any + register(registerWebviewConstructor) // 初始化bus和context2D useEffect(() => { - ConstructorsRegistry.register(registerWebviewConstructor) - const webviewPostMessage = (message: WebviewMessage) => { if (canvasRef.current.webview) { canvasRef.current.webview.postMessage(JSON.stringify(message)) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx index 6675f0621c..8749001213 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/utils.tsx @@ -144,7 +144,7 @@ export const useWebviewBinding = ({ registerWebviewProperties(instanceRef.current as Instance, properties) registerWebviewMethods(instanceRef.current as Instance, methods) } - }, [targetName, properties, methods]) + }, []) return instanceRef } From 2ed7e7cc42cf9828ffdbc0a3758e1aaa4dce2907 Mon Sep 17 00:00:00 2001 From: lareinayanyu Date: Wed, 20 Nov 2024 22:19:15 +0800 Subject: [PATCH 26/26] =?UTF-8?q?chore:=20=E4=BF=AE=E6=94=B9webview?= =?UTF-8?q?=E9=80=9A=E4=BF=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/react/mpx-canvas/html.ts | 433 +++++++++--------- .../components/react/mpx-canvas/webview.js | 317 +++++++++++++ 2 files changed, 534 insertions(+), 216 deletions(-) create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/webview.js diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/html.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/html.ts index 9fb3816d6f..de788a84f0 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/html.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/html.ts @@ -54,136 +54,116 @@ function autoScaleCanvas(canvas) { } window.autoScaleCanvas = autoScaleCanvas; - diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/webview.js b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/webview.js new file mode 100644 index 0000000000..7e37482354 --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-canvas/webview.js @@ -0,0 +1,317 @@ +/** + * Extracted from https://github.com/iddan/react-native-canvas + */ +const WEBVIEW_TARGET = '@@WEBVIEW_TARGET' + +const ID = () => Math.random().toString(32).slice(2) + +const flattenObjectCopyValue = (flatObj, srcObj, key) => { + const value = srcObj[key] + if (typeof value === 'function') { + return + } + if (typeof value === 'object' && value instanceof Node) { + return + } + flatObj[key] = flattenObject(value) +} + +const flattenObject = object => { + if (typeof object !== 'object' || object === null) { + return object + } + // 处理 TypedArray + if (object instanceof Uint8ClampedArray) { + return Array.from(object) + } + const flatObject = {} + for (const key in object) { + flattenObjectCopyValue(flatObject, object, key) + } + for (const key in Object.getOwnPropertyNames(object)) { + flattenObjectCopyValue(flatObject, object, key) + } + return flatObject +} + +class AutoScaledCanvas { + constructor (element) { + this.element = element + } + + toDataURL (...args) { + return this.element.toDataURL(...args) + } + + autoScale () { + if (this.savedHeight !== undefined) { + this.element.height = this.savedHeight + } + if (this.savedWidth !== undefined) { + this.element.width = this.savedWidth + } + window.autoScaleCanvas(this.element) + } + + get width () { + return this.element.width + } + + set width (value) { + this.savedWidth = value + this.autoScale() + return value + } + + get height () { + return this.element.height + } + + set height (value) { + this.savedHeight = value + this.autoScale() + return value + } +} + +const toMessage = result => { + if (result instanceof Blob) { + return { + type: 'blob', + payload: btoa(result), + meta: {} + } + } + if (result instanceof Object) { + if (!result[WEBVIEW_TARGET]) { + const id = ID() + result[WEBVIEW_TARGET] = id + targets[id] = result + } + return { + type: 'json', + payload: flattenObject(result), + args: toArgs(flattenObject(result)), + meta: { + target: result[WEBVIEW_TARGET], + constructor: result.__constructorName__ || result.constructor.name + } + } + } + return { + type: 'json', + payload: typeof result === 'string' ? result : JSON.stringify(result), + meta: {} + } +} + +/** + * Gets the all the args required for creating the object. + * Also converts typed arrays to normal arrays. + * + * For example with ImageData we need a Uint8ClampedArray, + * but if we sent it as JSON it will be sent as an object + * not an array. So we convert any typed arrays into arrays + * first, they will be converted to Uint8ClampedArrays in + * `webview-binders.js`. + * + */ +const toArgs = result => { + const args = [] + for (const key in result) { + if (result[key] !== undefined && key !== '@@WEBVIEW_TARGET') { + args.push(result[key]) + } + } + return args +} + +/** + * Creates objects from args. If any argument have the object + * which contains `className` it means we need to convert that + * argument into an object. + * + * We need to do this because when we pass data between the WebView + * and RN using JSON, it strips/removes the class data from the object. + * So this will raise errors as the WebView will expect arguments to be + * of a certain class. + * + * For example for ImageData we expect to receive + * [{className: Uint8ClampedArray, classArgs: [Array(4)]}, 100, 100] + * We need to convert the first parameter into an object first. + * + */ +const createObjectsFromArgs = args => { + for (let index = 0; index < args.length; index += 1) { + const currentArg = args[index] + if (currentArg && currentArg.className !== undefined) { + const { className, classArgs } = currentArg + // new ImageData,第一个参数需要是 Uint8ClampedArray + const object = new constructors[className](...classArgs) + args[index] = object + } + } + return args +} + +const canvas = document.createElement('canvas') +const autoScaledCanvas = new AutoScaledCanvas(canvas) + +const targets = { + canvas: autoScaledCanvas, + context2D: canvas.getContext('2d') +} + +const constructors = { + CanvasGradient, + Image, + ImageData, + Uint8ClampedArray +} + +/** + * In iOS 9 constructors doesn't have bind defined which fails + * Babel object constructors utility function + */ +Image.bind = + Image.bind || + function () { + return Image + } + +ImageData.bind = + ImageData.bind || + function () { + return ImageData + } + +Uint8ClampedArray.bind = + Uint8ClampedArray.bind || + function () { + return Uint8ClampedArray + } + +const populateRefs = arg => { + if (arg && arg.__ref__) { + return targets[arg.__ref__] + } + return arg +} + +document.body.appendChild(canvas) + +/** + * NOTE: Depending on the message type, the message sender will potentially get a callback via + * window.ReactNativeWebView.postMessage(...). The postMessage function causes Bus to resolve + * a Promise to the caller. + * + * For example, ctx.fillRect(...) returns a Promise that is then resolved from the code below. + * + * 'set' is currently the exception - it doesn't resolve at all. + * Therefore, Bus should not be saving message ids for 'set' messages. + * See the function 'post' in Bus.js. + */ +function handleMessage ({ id, type, payload }) { + switch (type) { + case 'exec': { + const { target, method, args } = payload + const result = targets[target][method](...args.map(populateRefs)) + const message = toMessage(result) + + /** + * In iOS 9 some classes name are not defined so we compare to + * known constructors to find the name. + */ + if (typeof result === 'object' && !message.meta.constructor) { + for (const constructorName in constructors) { + if (result instanceof constructors[constructorName]) { + message.meta.constructor = constructorName + } + } + } + window.ReactNativeWebView.postMessage(JSON.stringify({ id, ...message })) + break + } + case 'set': { + const { target, key, value } = payload + targets[target][key] = populateRefs(value) + break + } + case 'construct': { + const { constructor, id: target, args = [] } = payload + const newArgs = createObjectsFromArgs(args) + let object + try { + object = new constructors[constructor](...newArgs) + } catch (error) { + throw new Error( + `Error while constructing ${constructor} ${error.message}` + ) + } + object.__constructorName__ = constructor + const message = toMessage({}) + targets[target] = object + window.ReactNativeWebView.postMessage(JSON.stringify({ id, ...message })) + break + } + case 'listen': { + const { types, target } = payload + for (const eventType of types) { + targets[target].addEventListener(eventType, e => { + const message = toMessage({ + type: 'event', + payload: { + type: e.type, + target: { + ...flattenObject(targets[target]), + [WEBVIEW_TARGET]: target + } + } + }) + window.ReactNativeWebView.postMessage( + JSON.stringify({ id, ...message }) + ) + }) + } + break + } + } +} + +const handleError = (err, message) => { + window.ReactNativeWebView.postMessage( + JSON.stringify({ + id: message.id, + type: 'error', + payload: { + message: err.message, + stack: err.stack + } + }) + ) + document.removeEventListener('message', handleIncomingMessage) +} + +function handleIncomingMessage (e) { + const data = JSON.parse(e.data) + if (Array.isArray(data)) { + for (const message of data) { + try { + handleMessage(message) + } catch (err) { + handleError(err, message) + } + } + } else { + try { + handleMessage(data) + } catch (err) { + handleError(err, data) + } + } +} + +// iOS +window.addEventListener('message', handleIncomingMessage) +// Android +document.addEventListener('message', handleIncomingMessage)