Skip to content

Commit

Permalink
Merge pull request #1708 from didi/feat-rn-canvas
Browse files Browse the repository at this point in the history
Feat rn canvas
  • Loading branch information
hiyuki authored Nov 21, 2024
2 parents 193da9f + 7bd3b0d commit 6e549b0
Show file tree
Hide file tree
Showing 13 changed files with 1,441 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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$/,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { warn } from '@mpxjs/utils'
interface Message {
id?: string
type: string
payload?: any
}
export default class Bus {
_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: Message): Promise<any> {
return new Promise((resolve) => {
if (message.type !== 'set' && message.id) {
this._messageListeners[message.id] = resolve
}

if (!this._paused) {
this._send(message)
} else {
this._queue.push(message)
}
})
}

handle (message: Message): void {
if (!message.id) return
const handler = this._messageListeners[message.id]
delete this._messageListeners[message.id]

if (handler) {
handler(message)
} else {
warn(`Received unexpected message: ${message}`)
}
}

pause (): void {
this._paused = true
}

resume (): void {
this._paused = false
this._send(this._queue)
this._queue = []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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)
if (this.onConstruction && !noOnConstruction) {
this.onConstruction()
}
}

postMessage (message: WebviewMessage) {
return this.canvas.postMessage(message)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { CanvasInstance, 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',
lineWidth: 1.0,
miterLimit: 10.0,
shadowBlur: 0,
shadowColor: 'rgba(0,0,0,0)',
shadowOffsetX: 0,
shadowOffsetY: 0,
strokeStyle: '#000',
textAlign: 'start',
textBaseline: 'alphabetic',
textRendering: 'auto',
wordSpacing: '0px'
}

const METHODS = [
'arc',
'arcTo',
'beginPath',
'bezierCurveTo',
'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',
'setTransform',
'stroke',
'strokeRect',
'strokeText',
'transform',
'translate'
]
export default class CanvasRenderingContext2D {
canvas: CanvasInstance
constructor (canvas: CanvasInstance) {
this.canvas = canvas
registerWebviewTarget(this, 'context2D')
registerWebviewProperties(this, PROPERTIES)
registerWebviewMethods(this, METHODS)
}

postMessage (message: WebviewMessage) {
return this.canvas.postMessage(message)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { WebviewMessage, WEBVIEW_TARGET, registerWebviewProperties, CanvasInstance } from './utils'

const PROPERTIES = {
crossOrigin: undefined,
height: undefined,
src: undefined,
width: undefined
}

export class Image {
[WEBVIEW_TARGET]: string;
canvas: any;
width: number;
height: number;
private _loadListener: any;
private _errorListener: any;
private _onload: ((...args: any[]) => void);
private _onerror: ((...args: any[]) => void);
[key: string]: any;

constructor (canvas: CanvasInstance, width?: number, height?: number, noOnConstruction = false) {
this.canvas = canvas
registerWebviewProperties(this, PROPERTIES)

if (width) {
this.width = width
}
if (height) {
this.height = height
}

if (this.onConstruction && !noOnConstruction) {
this.onConstruction()
this.postMessage({
type: 'listen',
payload: {
types: ['error', 'load'],
target: this[WEBVIEW_TARGET]
}
})
}
}

postMessage (message: WebviewMessage) {
return this.canvas.postMessage(message)
}

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' &&
target[WEBVIEW_TARGET] === this[WEBVIEW_TARGET] &&
message.payload.type === type
) {
for (const key in target) {
const value = target[key]
if (key in this && this[key] !== value) {
this[key] = value
}
}
callbackFn({
...message.payload,
target: this
})
}
})
}

set onload (callback: ((...args: any[]) => void)) {
this._onload = callback
if (this._loadListener) {
this.canvas.removeMessageListener(this._loadListener)
}
if (callback) {
this._loadListener = this.addEventListener('load', callback)
}
}

get onload (): ((...args: any[]) => void) {
return this._onload
}

set onerror (callback: ((...args: any[]) => void)) {
this._onerror = callback
if (this._errorListener) {
this.canvas.removeMessageListener(this._errorListener)
}
if (callback) {
this._errorListener = this.addEventListener('error', callback)
}
}

get onerror () : ((...args: any[]) => void) {
return this._onerror
}
}

export function createImage (canvas: CanvasInstance, width?: number, height?: number) {
return new Image(canvas, width, height)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {
WebviewMessage,
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) {
this.onConstruction(dataArray, width, height)
}
}

postMessage = (message: WebviewMessage) => {
return this.canvas.postMessage(message)
};
}

export function createImageData (canvas: CanvasInstance, dataArray: number[], width: number, height: number) {
return new ImageData(canvas, dataArray, width, height)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Image } from './Image'
import CanvasGradient from './CanvasGradient'
import ImageData from './ImageData'
import { WebviewConstructor } from './utils'

export enum ConstructorType {
Image = 'Image',
CanvasGradient = 'CanvasGradient',
ImageData = 'ImageData'
}

interface Constructor {
type: ConstructorType
instance: WebviewConstructor
}

const constructors: Constructor[] = [
{ type: ConstructorType.Image, instance: Image },
{ type: ConstructorType.CanvasGradient, instance: CanvasGradient },
{ type: ConstructorType.ImageData, instance: ImageData }
]

export function useConstructorsRegistry () {
const register = (registerWebviewConstructor: Function): void => {
constructors.forEach(({ type, instance }) => {
registerWebviewConstructor(instance, type)
})
}

const getConstructor = (type: ConstructorType): WebviewConstructor | undefined => {
return constructors.find(c => c.type === type)?.instance
}

return {
register,
getConstructor
}
}
Loading

0 comments on commit 6e549b0

Please sign in to comment.