@duxapp/react-native-canvas
v0.1.1
Published
Pure JavaScript canvas-like API for React Native powered by react-native-skia
Readme
@duxapp/react-native-canvas
@duxapp/react-native-canvas 是一个基于 @shopify/react-native-skia 的 React Native canvas 绘图库,提供 Canvas、Path2D、OffscreenCanvas、Image 以及接近 Canvas 2D 的绘制 API。当前只支持 2d,不支持 WebGL 或其他 3D 渲染上下文。
目录
- 主要特性
- 安装
- 快速开始
- 基本使用方式
- 示例
- 性能说明
- 详细文档说明
- 导出内容
- API 概览
- Picture 模式
Path2DOffscreenCanvasImageuseClickabledrawImage()支持的 sourcetoDataURL()- 兼容性说明
- 已知限制
- 类型文件
- License
主要特性
- React Native
Canvas组件 - 类 Canvas 2D context API
- 仅支持 2D,不支持 WebGL / 3D 上下文
Path2DOffscreenCanvasImageuseClickable- 文本绘制与测量
- 变换、状态栈
- 渐变、图案、阴影、混合模式
toDataURL()- 推荐开启
picture模式,可明显提升大多数绘制场景下的帧率 - 内部已经处理 DPI,不要再额外做 PixelRatio / devicePixelRatio 缩放
安装
yarn add @duxapp/react-native-canvas @shopify/react-native-skia或
npm install @duxapp/react-native-canvas @shopify/react-native-skia必须安装的依赖:
@shopify/react-native-skia
这个包本身是纯 JS,但宿主工程必须正确安装 @shopify/react-native-skia。
快速开始
import { useEffect } from 'react'
import { Canvas, useCanvasRef } from '@duxapp/react-native-canvas'
export default function Demo() {
const ref = useCanvasRef()
useEffect(() => {
let mounted = true
ref.current?.getCanvas().then(({ canvas, size }) => {
if (!mounted) return
const ctx = canvas.getContext('2d')
ctx.fillStyle = '#f5f5f5'
ctx.fillRect(0, 0, size.width, size.height)
ctx.beginPath()
ctx.moveTo(20, 20)
ctx.lineTo(180, 20)
ctx.lineTo(100, 120)
ctx.closePath()
ctx.fillStyle = '#2f80ed'
ctx.strokeStyle = '#0f4aa1'
ctx.lineWidth = 4
ctx.fill()
ctx.stroke()
ctx.font = 'bold 20px sans-serif'
ctx.fillStyle = '#111'
ctx.fillText('Hello Canvas', 20, 170)
})
return () => {
mounted = false
}
}, [ref])
return <Canvas ref={ref} style={{ flex: 1, minHeight: 240 }} />
}示例
import { useEffect } from 'react'
import { Canvas, Path2D, useCanvasRef } from '@duxapp/react-native-canvas'
export default function Example() {
const ref = useCanvasRef()
useEffect(() => {
ref.current?.getCanvas().then(({ canvas }) => {
const ctx = canvas.getContext('2d')
const path = new Path2D()
path.moveTo(40, 40)
path.lineTo(160, 40)
path.lineTo(100, 140)
path.closePath()
ctx.fillStyle = '#e8f1ff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = '#2f80ed'
ctx.strokeStyle = '#1456b8'
ctx.lineWidth = 3
ctx.fill(path)
ctx.stroke(path)
})
}, [ref])
return <Canvas ref={ref} style={{ flex: 1 }} />
}Picture + 动画示例
import { useEffect } from 'react'
import { Canvas, useCanvasRef } from '@duxapp/react-native-canvas'
export default function PictureAnimationExample() {
const ref = useCanvasRef()
useEffect(() => {
let frameId = 0
let active = true
ref.current?.getCanvas().then(({ canvas, size }) => {
if (!active) return
const ctx = canvas.getContext('2d')
let x = 30
let direction = 1
const render = () => {
if (!active) return
ctx.fillStyle = '#f7f8fa'
ctx.fillRect(0, 0, size.width, size.height)
ctx.beginPath()
ctx.arc(x, size.height / 2, 24, 0, Math.PI * 2)
ctx.fillStyle = '#2f80ed'
ctx.fill()
x += direction * 2
if (x >= size.width - 30 || x <= 30) {
direction *= -1
}
frameId = requestAnimationFrame(render)
}
render()
})
return () => {
active = false
cancelAnimationFrame(frameId)
}
}, [ref])
return <Canvas ref={ref} picture style={{ flex: 1, minHeight: 240 }} />
}性能说明
当前性能大致可以这样理解:
- 和现有的 React Native canvas 插件,例如
react-native-canvas、react-native-gcanvas相比,在相同类型的绘制场景下,这个库通常会有非常明显的性能提升 - 但和原生 canvas 相比,仍然还有很大的差距
- 在相同场景下,目前的表现通常还是明显低于原生 canvas 的一半
这意味着:
- 如果你现在使用的是较旧的 RN canvas 方案,迁移过来通常会有很明显的收益
- 如果你的目标是接近原生 canvas 性能,目前仍然要预期存在较大的差距
- 在渲染模型允许的情况下,强烈建议开启
picture模式,以尽可能发挥当前实现的帧率表现
详细文档说明
下面的章节会继续说明这个包的 API、渲染模式、支持的绘制源、兼容性说明以及已知限制。
基本使用方式
1. 使用 useCanvasRef 创建 ref
const ref = useCanvasRef()这个 ref 直接传给 Canvas:
<Canvas ref={ref} style={{ flex: 1 }} />2. 获取 canvas 实例
const { canvas, size } = await ref.current.getCanvas()返回值说明:
canvas:canvas 风格对象size:当前布局信息{ width, height, x, y }
3. 获取 2D context
const ctx = canvas.getContext('2d')导出内容
import {
Canvas,
Image,
OffscreenCanvas,
Path2D,
defineCanvas,
defineCanvasContext,
useCanvasRef,
useClickable
} from '@duxapp/react-native-canvas'说明:
Canvas:React Native canvas 组件useCanvasRef:返回Canvas所需 ref 的 hookPath2D:路径对象OffscreenCanvas:离屏画布Image:用于drawImage()的图片对象useClickable:独立的 hook,用于在 React Native 上模拟网页端触摸事件defineCanvas/defineCanvasContext:为了兼容保留下来的 identity helper
useClickable
useClickable 是从原始 RN 实现中抽出来的独立 hook,它的作用是在 React Native 上模拟网页端触摸事件,让一些库可以更少改动地运行。它没有接入 Canvas 内部。
import { Canvas, useCanvasRef, useClickable } from '@duxapp/react-native-canvas'
export default function Demo() {
const ref = useCanvasRef()
const clickable = useClickable({
onClick: e => {
console.log('click', e.detail.x, e.detail.y)
},
onLongPress: e => {
console.log('longpress', e)
}
})
return <Canvas ref={ref} {...clickable} style={{ flex: 1, minHeight: 240 }} />
}支持这些回调:
onClickonLongPressonTouchStartonTouchMoveonTouchEndonTouchCancel
API 概览
Canvas 组件
Props:
styleonLayoutpicture
picture 用于启用 React Native 的 picture 渲染路径,并且在大多数场景下都推荐开启。
Canvas Ref
ref.current.getCanvas(): Promise<{
canvas: CanvasElement
size: {
width: number
height: number
x: number
y: number
}
}>Canvas Element
返回的 canvas 支持:
getContext('2d')createImageData(width, height)toDataURL(type?, encoderOptions?)widthheight
当前只支持 getContext('2d')。不支持 WebGL、WebGL2 以及其他 3D 渲染上下文。
DPI 处理说明
设备像素比已经在内部处理完成。
不要再手动对 canvas 尺寸、坐标或变换额外乘这些值:
PixelRatio.get()window.devicePixelRatio- 自己定义的 DPR 缩放系数
正常情况下,直接按逻辑布局尺寸进行绘制即可。
2D Context
当前支持的能力分组:
- 状态:
save、restore - 变换:
translate、scale、rotate、transform、setTransform、resetTransform、getTransform - 路径:
beginPath、closePath、moveTo、lineTo、arc、arcTo、bezierCurveTo、quadraticCurveTo、rect、roundRect、ellipse - 绘制:
fill、stroke、clip、isPointInPath - 矩形:
fillRect、strokeRect、clearRect - 文本:
fillText、strokeText、measureText - 图像:
drawImage - 像素:
getImageData、putImageData - 样式:
fillStyle、strokeStyle、lineWidth、lineCap、lineJoin、miterLimit、setLineDash、lineDashOffset - 透明度与阴影:
globalAlpha、shadowColor、shadowBlur、shadowOffsetX、shadowOffsetY - 文本样式:
font、textAlign、textBaseline、direction - 合成:
globalCompositeOperation - 渐变与图案:
createLinearGradient、createRadialGradient、createPattern
Picture 模式
开启方式:
<Canvas ref={ref} picture style={{ flex: 1 }} />这个模式通常可以明显提升绘制帧率,建议默认开启,除非你的场景强依赖持久 canvas 状态下的增量更新语义。
推荐使用方式
- 大多数 canvas 绘制场景都建议默认开启
- 特别适合图表、海报、预览、编辑器整帧刷新这类场景
- 只有在你依赖持久画布状态做局部增量更新时,才建议评估是否关闭
默认优点
- 通常可以获得更高的绘制帧率
- 特别适合动画和整帧重绘场景
- 当每一帧都从头计算时,渲染模型会更简单
需要注意的问题
- picture 绘制不会保留历史内容
- 只会保留当前这一帧绘制出来的内容
- 到下一个时刻开始绘制时,上一帧的内容会自动被清除
- 绘制状态不会在帧之间自动保留
- 变换状态不会在帧之间自动保留
clearRect()不再等价于持久画布上的增量擦除
如果你的场景依赖基于历史内容的局部更新,通常不适合使用这个模式。
Path2D
import { Path2D } from '@duxapp/react-native-canvas'
const path = new Path2D()
path.moveTo(20, 20)
path.lineTo(100, 20)
path.lineTo(60, 80)
path.closePath()
ctx.fill(path)
ctx.stroke(path)也支持通过另一个 Path2D 或 SVG path 字符串初始化。
OffscreenCanvas
import { OffscreenCanvas } from '@duxapp/react-native-canvas'
const offscreen = new OffscreenCanvas(200, 120)
const ctx = offscreen.getContext('2d')
ctx.fillStyle = '#000'
ctx.fillRect(0, 0, 200, 120)
const dataUrl = offscreen.toDataURL()适合场景:
- 预渲染
- 生成纹理或缩略图
- 作为
drawImage()的绘制源 - 用于
createPattern()图案源
Image
import { Image } from '@duxapp/react-native-canvas'
const image = new Image()
image.onload = () => {
ctx.drawImage(image, 0, 0, 120, 120)
}
image.onerror = err => {
console.error(err)
}
image.src = 'https://example.com/example.png'支持的 src 类型:
- 远程 URL 字符串
data:URL 字符串require(...)返回的本地资源 id
当前支持的成员:
srconloadonerroronabortaltcompletecurrentSrcwidthheightdecode()addEventListener()removeEventListener()
drawImage() 支持的 source
当前类型和运行时都支持:
ImageOffscreenCanvas
例如:
const offscreen = new OffscreenCanvas(100, 100)
const offscreenCtx = offscreen.getContext('2d')
offscreenCtx.fillStyle = 'red'
offscreenCtx.fillRect(0, 0, 100, 100)
ctx.drawImage(offscreen, 20, 20)toDataURL()
主 canvas 对象和 OffscreenCanvas 都支持 toDataURL():
const url = canvas.toDataURL()
const jpeg = canvas.toDataURL('image/jpeg', 0.9)支持的输出类型:
image/pngimage/jpegimage/webp
兼容性说明
这个库是 canvas-like,不是浏览器完整的 HTMLCanvasElement 实现。
和浏览器相比,有一些有意保留的差异:
- 没有 DOM API
- 没有完整浏览器图片加载行为
- 没有浏览器事件系统
- 这个包版本不包含小程序兼容 API
如果某些浏览器 API 在 React Native 里没有实际意义,就不会强行暴露。
已知限制
- 某些边界场景下行为可能与浏览器 canvas 存在差异
- 文本测量依赖 Skia 的字体行为
- 图片加载依赖 React Native / Skia 运行环境
- picture 模式针对整帧重绘优化,不适合增量更新
- 不是所有浏览器 canvas API 都已实现
类型文件
包内提供 TypeScript 类型:
- 英文注释版本:
src/index.d.ts - 中文注释版本:
src/index.zh-CN.d.ts
License
MIT
