Skip to content

Commit

Permalink
Feat/canvas (#15319)
Browse files Browse the repository at this point in the history
* feat: 新建canvas组件

* feat: 完成context适配器

* feat: 完成canvas所有绘制

* feat: 添加draw方法的实现

* feat: 为window添加pixelRatio

* fix: 修改引用路径

---------

Co-authored-by: mayintao3 <[email protected]>
  • Loading branch information
yoturg and mayintao3 authored Mar 14, 2024
1 parent 9bffe91 commit 4c5e4c3
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 43 deletions.
2 changes: 1 addition & 1 deletion packages/taro-platform-harmony/src/apis/base/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const getSystemInfoSync: typeof Taro.getSystemInfoSync = function () {
res.notificationSoundAuthorized = false // 通知带有声音的开关(仅 iOS 有效)boolean
res.phoneCalendarAuthorized = null // 使用日历的开关 boolean
res.wifiEnabled = false // Wi-Fi 的系统开关 boolean
res.pixelRatio = display && (Math.round(display.xDPI / display.width * 100) / 100) // 设备像素比,number
res.pixelRatio = display && display.densityPixels // 设备像素比,number
res.platform = 'harmony' // 客户端平台 string
res.safeArea = safeArea // 在竖屏正方向下的安全区域 General.SafeAreaResult
res.screenHeight = display?.height // 屏幕高度,单位px number
Expand Down
10 changes: 9 additions & 1 deletion packages/taro-platform-harmony/src/apis/canvas/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { eventSource } from '@tarojs/runtime'

import { temporarilyNotSupport } from '../utils'

import type { TaroCanvasElement } from '@tarojs/runtime'
// 画布

/** 创建离屏 canvas 实例 */
export const createOffscreenCanvas = /* @__PURE__ */ temporarilyNotSupport('createOffscreenCanvas')

/** 创建 canvas 的绘图上下文 CanvasContext 对象 */
export const createCanvasContext = /* @__PURE__ */ temporarilyNotSupport('createOffscreenCanvas')
// export const createCanvasContext = /* @__PURE__ */ temporarilyNotSupport('createOffscreenCanvas')
export const createCanvasContext = (canvasId: string) => {
const dom = eventSource.get(`canvasId-${canvasId}`)
// return dom as TaroCanvasElement
if (dom) return (dom as unknown as TaroCanvasElement).context
}

/** 把当前画布指定区域的内容导出生成指定大小的图片 */
export const canvasToTempFilePath = /* @__PURE__ */ temporarilyNotSupport('createOffscreenCanvas')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { TaroCanvasElement } from '@tarojs/runtime'
import { cancelAnimationFrame, requestAnimationFrame } from '@tarojs/runtime'


@Component
export default struct TaroCanvas {
@ObjectLink node: TaroCanvasElement
rafId: number = 0


aboutToDisappear() {
if(this.rafId) {
cancelAnimationFrame(this.rafId)
}
}

build() {
Canvas(this.node._context)
.width('100%')
.height('100%')
.backgroundColor('#ffff00')
.onReady(() => {
const context = this.node._context

const draw = () => {
if (this.node._drawList.length) {
while (this.node._drawList.length) {
const item = this.node._drawList.shift()
if (item) {
if (typeof context[item.key] === 'function') {
context[item.key](...[].concat(item.value))
} else {
context[item.key] = item.value
}
}
}
}
this.rafId = requestAnimationFrame(draw)
}
draw()
})
}
}
5 changes: 5 additions & 0 deletions packages/taro-platform-harmony/src/runtime-ets/bom/window.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ohosWindow from '@ohos.window'
import { History, Location } from '@tarojs/runtime/dist/runtime.esm'
import { getSystemInfoSync } from '@tarojs/taro'

import { TaroEventTarget } from '../dom/eventTarget'
import { getComputedStyle } from './getComputedStyle'
Expand Down Expand Up @@ -30,6 +31,10 @@ class Window extends TaroEventTarget {
return this._doc
}

get devicePixelRatio () {
return getSystemInfoSync().pixelRatio
}

setTimeout (...args: Parameters<typeof setTimeout>) {
setTimeout(...args)
}
Expand Down
136 changes: 136 additions & 0 deletions packages/taro-platform-harmony/src/runtime-ets/dom/element/canvas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { eventSource, TaroAny, TaroNode } from '@tarojs/runtime'

import { TaroElement } from './element'

import type { CanvasProps, CanvasTouchEvent } from '@tarojs/components/types'

export class CanvasRenderingContext2DWXAdapter extends CanvasRenderingContext2D {
constructor(settings?: RenderingContextSetting) {

Check failure on line 8 in packages/taro-platform-harmony/src/runtime-ets/dom/element/canvas.ts

View workflow job for this annotation

GitHub Actions / Testing on Node.js 16.x (ubuntu-latest)

Useless constructor

Check failure on line 8 in packages/taro-platform-harmony/src/runtime-ets/dom/element/canvas.ts

View workflow job for this annotation

GitHub Actions / Testing on Node.js 18.x (ubuntu-latest)

Useless constructor

Check failure on line 8 in packages/taro-platform-harmony/src/runtime-ets/dom/element/canvas.ts

View workflow job for this annotation

GitHub Actions / Testing on Node.js 20.x (macos-11)

Useless constructor

Check failure on line 8 in packages/taro-platform-harmony/src/runtime-ets/dom/element/canvas.ts

View workflow job for this annotation

GitHub Actions / Testing on Node.js 20.x (windows-latest)

Useless constructor

Check failure on line 8 in packages/taro-platform-harmony/src/runtime-ets/dom/element/canvas.ts

View workflow job for this annotation

GitHub Actions / Testing on Node.js 20.x (ubuntu-latest)

Useless constructor
super(settings)
}

createCircularGradient() {
// Not supported now
}

draw(cb?: (...args: any[]) => any) {
typeof cb === 'function' && cb()
// Not supported now
}

setFillStyle(fillStyle: typeof this.fillStyle) {
this.fillStyle = fillStyle
}

setFontSize(fontSize: number) {
const font = this.font.split(' ')
font[2] = `${fontSize}`
this.font = font.join(' ')
}

setGlobalAlpha(globalAlpha: typeof this.globalAlpha) {
this.globalAlpha = globalAlpha
}

setLineCap(lineCap: typeof this.lineCap) {
this.lineCap = lineCap
}

setLineJoin(lineJoin: typeof this.lineJoin) {
this.lineJoin = lineJoin
}

setLineWidth(lineWidth: typeof this.lineWidth) {
this.lineWidth = lineWidth
}

setMiterLimit(miterLimit: typeof this.miterLimit) {
this.miterLimit = miterLimit
}

setShadow(offsetX: number, offsetY: number, blur: number, color: string) {
this.shadowOffsetX = offsetX
this.shadowOffsetY = offsetY
this.shadowBlur = blur
this.shadowColor = color
}

setStrokeStyle(strokeStyle: typeof this.strokeStyle) {
this.strokeStyle = strokeStyle
}

setTextAlign(textAlign: typeof this.textAlign) {
this.textAlign = textAlign
}

setTextBaseline(textBaseline: typeof this.textBaseline) {
this.textBaseline = textBaseline
}
}
function getContextKey(obj) {
let currentObj = obj
let res = []
while (currentObj) {
if (currentObj instanceof CanvasRenderingContext2D) {
res = [...res, ...Object.getOwnPropertyNames(currentObj)]
}
currentObj = Object.getPrototypeOf(currentObj)
}
return res
}

@Observed
export class TaroCanvasElement extends TaroElement<CanvasProps, CanvasTouchEvent> {
_drawList: {
key: string
value: TaroAny
}[] = []

settings: RenderingContextSettings
_context: CanvasRenderingContext2D
_contextProxy: CanvasRenderingContext2D

constructor() {
super('Canvas')
this.settings = new RenderingContextSettings(true)
const context = new CanvasRenderingContext2DWXAdapter(this.settings) as CanvasRenderingContext2D
this._context = context

const proxyObj = getContextKey(context).reduce((obj, key) => {
if (typeof context[key] === 'function') {
obj[key] = new Proxy(context[key], {
apply: (target, thisArg, argumentsList) => {
this._drawList.push({
key,
value: argumentsList,
})
},
})
} else {
obj[key] = context[key]
}
return obj
}, {})

this._contextProxy = new Proxy(proxyObj, {
set: (_, property, value) => {
this._drawList.push({
key: property,
value,
})
return true
},
})
}

get context() {
return this._contextProxy
}

public setAttribute(name: string, value: TaroAny): void {
if (name === 'canvasId') {
eventSource.set(`canvasId-${value}`, this as TaroNode)
}
super.setAttribute(name, value)
}
}
Loading

0 comments on commit 4c5e4c3

Please sign in to comment.