Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

一个轻量级的微信小程序 Canvas (type="2d") 渲染引擎

Notifications You must be signed in to change notification settings

chenlin666/mini-program-rc

Repository files navigation

mini-programs-rc

一个轻量级的微信小程序 Canvas (type="2d") 渲染引擎

特性

  • 高性能且松耦合的渲染架构
  • 超轻量级的代码体积
  • 支持 Canvas 元素管理
  • 支持 Canvas 元素事件体系
  • 完备的 group 嵌套体系
  • 支持可以变形的 clip 裁剪体系
  • 内置文本、位图、绘图对象和多种矢量绘制对象
  • 内置图片加载器

快捷访问

一分钟入门使用

下载本项目到本地,通过 npm 下载依赖,然后使用命令npm run build构建

拷贝 dist 目录下的 mian.js 到微信小程序项目中。

在 page 或者 component 中使用

在的 wxml 里声明 canvas 和声明点击事件判断的 canvas

小程序不支持虚拟 canvas,点击事件判断由 hit-canvas 渲染判断

<view class="container">
 <view class="container">
 <canvas
 bindtouchend="touchend"
 bindtouchmove="touchmove"
 bindtouchstart="touchstart"
 class="canvas"
 id="canvas"
 style="width:{{width}}px;height:{{height}}px"
 type="2d"
 ></canvas>
 <!-- 隐藏hit-canvas -->
 <canvas
 class="hit-canvas"
 id="hitCanvas"
 style="width:{{width}}px;height:{{height}}px"
 type="2d"
 ></canvas>
 </view>
</view>

在 wxss 中隐藏 class="hit-canvas"

.hit-canvas {
 position: fixed;
 top: -999rpx;
 left: -999rpx;
}

在 js 中引入并初始化项目

import mprc from 'main.js';
const { Stage, Group, Graphics, Rect, Circle } = mprc;
Page({
 data: {
 width: 375,
 height: 300
 },
 onReady: async function () {
 const canvas = await this.getContainer('#canvas');
 const stage = new Stage(canvas, this.data.width, this.data.height);
 const hitCanvas = await this.getContainer('#hitCanvas');
 stage.setHitCanvas(hitCanvas);
 },
 // 事件监听
 touchstart: function (event) {
 stage.touchStartHandler(event);
 },
 touchmove: function (event) {
 stage.touchMoveHandler(event);
 },
 touchend: function (event) {
 stage.touchEndHandler(event);
 },
 // 获取canvas对象方法
 getContainer(id) {
 return new Promise((resolve, reject) => {
 const query = wx.createSelectorQuery();
 query
 .select(id)
 .fields({ node: true, size: true })
 .exec(res => {
 const canvas = res[0].node;
 resolve(canvas);
 });
 });
 }
});

初始化项目后绘制内容

// 集合容器,设置透明度
const group = new Group();
group.x = 50;
group.y = 50;
group.alpha = 0.8;
// 绘制矩形图形,设置透明度,透明度会与集合的透明度合并展示
const rect = new Rect(100, 200, {
 fillStyle: '#000000'
});
rect.alpha = 0.2;
rect.hitBox = [0, 0, 100, 200];
// 设置裁剪,将会对举行裁剪
const clipPath = new Graphics();
clipPath.arc(50, 50, 50, 0, Math.PI * 2);
rect.clip(clipPath);
// 绘制圆形图形,设置拖拽事件
const circle = new Circle(50, {
 fillStyle: 'red'
});
circle.on('drag', function (event) {
 circle.x += event.dx;
 circle.y += event.dy;
 stage.update();
});
// 将矩形和圆形图形放入集合之中
group.add(circle);
group.add(rect);
// 将集合放入根容器
stage.add(group);
// 更新渲染
stage.update();

查看项目示例或二次开发

npm run watch

用微信小程序编辑器导入该项目下的 weapp 文件。

内置对象

Group

用于分组,group 也可以嵌套 group,父容器的属性会叠加在子属性上, 例如:

  • group 的 x 是 100, group 里的 bitmap 的 x 是 200, 最后 bitmap 渲染到 stage 上的 x 是 300
  • group 的 alpha 是 0.7, group 里的 bitmap 的 alpha 是 0.6, 最后 bitmap 渲染到 stage 上的 alpha 是 0.42
const group = new Group();
const rect = new Rect(100, 100, {
 fillStyle: 'black'
});
group.add(rect);
stage.add(group);
stage.update();

group 拥有常用的 add 和 remove 方法进行元素的增加和删除。先 add 的会先绘制,所有后 add 的会盖在先 add 的上面。

Group 方法

add

添加对象

group.add(child);
remove

移除对象

group.remove(child);
empty

清空子对象

group.empty();
replace

使用一个对象替代子对象

group.replace(current, pre);

Stage

最大的顶层容器,继承自 Group,所以 Group 拥有的方法它全都有。

Stage 方法

update

任何元素添加到 Stage 上是看不到的,须要执行 update 方法。

任何元素属性的修改请执行 stage.update() 来更新渲染。

stage.update();
setHitCanvas

设置模拟虚拟 canvas,接受一个参数 canvas 对象,用于计算像素级的 touch 事件目标。

getTextWidth

获取要渲染文字的宽度,两个参数,第一个参数是 text: String,待绘制的文字,第二个参数是font: String,设置的文字的样式。

loadImage

Stage 内置图片加载器,接受一个参数url: string,返回一个 Promise 对象。

Promise 执行结果是 Image 对象,用于 bitmap 绘制。

const stage = new Stage(canvas, 200, 200);
const imgObj2 = await stage.loadImage('../logo.png');
const bitmap = new Bitmap(imgObj2);
stage.add(bitmap);
stage.updata();

Bitmap

Bitmap 接受一个参数,Image 对象的实例,不能使用 url 或者本地路径,bitmap 为同步,无回调方法。

const bitmap = new Bitmap(img);
stage.add(bitmap);
stage.update()

可以设置图片裁剪显示区域,和其他 transform 属性:

const bitmap = new Bitmap(img);
bitmap.x=50;
stage.add(bitmap);
const clipPath = new Graphics();
clipPath.rect(0, 0, 100, 200);
clipPath.x = 0;
clipPath.y = 50;
bitmap.clip(clipPath);
stage.add(bitmap);
stage.update()

Text

文本对象

const text = new Text(item.key, {
 font: `normal normal 20px Arial`,
 color: '#000000',
 baseline: 'bottom'
});

Text 方法

getWidth

获取文本宽度

textObj.getWidth();

Graphics

绘图对象,用于使用基本的连缀方式的 Canvas 指令绘制图形。

const graphics = new Graphics();
graphics
 .beginPath()
 .arc(0, 0, 10, 0, Math.PI * 2)
 .closePath()
 .fillStyle('#f4862c')
 .fill()
 .strokeStyle('black')
 .stroke();
graphics.x = 100;
graphics.y = 200;
stage.add(graphics);

特别注意,如果你在某个循环中执行 graphics 连缀绘制操作,请务必加上 clear() 方法,不然路径叠加到你的浏览器不堪重负:

setInterval(function () {
 graphics
 .clear()
 .beginPath()
 .arc(0, 0, 10, 0, Math.PI * 2)
 .stroke();
}, 16);

Shape

Rect

const rect = new Rect(200, 100, {
 fillStyle: 'black'
});

Circle

const circle = new Circle(10);

Ellipse

const ellipse = new Ellipse(120, 20);

属性

Transform

属性名 描述
x 水平偏移
y 竖直偏移
scaleX 水平缩放
scaleY 竖直缩放
scale 同时设置或读取 scaleX 和 scaleY
rotation 旋转
skewX 歪斜 X
skewY 歪斜 Y
regX 旋转基点 X
regY 旋转基点 Y

Alpha

属性名 描述
alpha 元素的透明度

如果父子都设置了 alpha 会进行乘法叠加。

compositeOperation

属性名 描述
compositeOperation 源图像绘制到目标图像上的叠加模式

注意这里如果自身没有定义 compositeOperation 会进行向上查找,找到最近的定义了 compositeOperation 的父容器作为自己的 compositeOperation。

Shadow

属性名 描述
shadow 阴影

使用方式:

obj.shadow = {
 color: '#42B035',
 offsetX: -5,
 offsetY: 5,
 blur: 10
};

Stage

Name Describe
stage 或者自己所在的 stage

使用方式:

obj.stage;

方法

destroy

销毁自己

obj.destroy();

注意:Group 销毁会销毁组中所有对象

事件

事件名 描述
tap 手指触摸后马上离开
touchstart 手指触摸动作开始
touchmove 手指触摸后移动
touchend 手指触摸动作结束
drag 拖拽

事件触发精确到像素级。如果要使用元素的矩形区域为点击区域,则需要设置设置元素的 hitBox 。

裁剪

const stage = new Stage(600, 400, 'body');
const bitmap = new Bitmap(imgObj2);
const clipPath = new Graphics();
clipPath.arc(40, 40, 25, 0, Math.PI * 2);
bitmap.clip(clipPath);
stage.add(bitmap);

使用下面的代码可以得到同样的效果:

const stage = new Stage(600, 400, 'body');
const bitmap = new Bitmap(imgObj2);
const clipPath = new Graphics();
clipPath.x = 40;
clipPath.y = 40;
clipPath.arc(0, 0, 25, 0, Math.PI * 2);
bitmap.clip(clipPath);
stage.add(bitmap);

裁剪区域同样支持所有 transform 属性(x,y,scaleX,scaleY,rotation,skewX,skewY,regX,regY)。

自定义对象

自定义 Shape

自定义 Shape 继承自 Shape:

class Sector extends Shape {
 constructor(r, from, to, option) {
 super();
 this.option = option || {};
 this.r = r;
 this.from = from;
 this.to = to;
 }
 draw() {
 this.beginPath()
 .moveTo(0, 0)
 .arc(0, 0, this.r, this.from, this.to)
 .closePath()
 .fillStyle(this.option.fillStyle)
 .fill()
 .strokeStyle(this.option.strokeStyle)
 .lineWidth(this.option.lineWidth)
 .stroke();
 }
}

使用 Shape:

const sector = new Sector(10, 0, Math.PI/6, {
 fillStyle: 'red'
 lineWidth: 2
})
stage.add(sector)
stage.update()

自定义 Element

自定义 Element 继承自 Group:

class Button extends Group {
 constructor (option) {
 super()
 this.width = option.width
 this.roundedRect = new RoundedRect(option.width, option.height, option.r)
 this.text = new Text(option.text, {
 font: option.font,
 color: option.color
 })
 this.text.x = option.width / 2 - this.text.getWidth() / 2 * this.text.scaleX
 this.text.y = option.height / 2 - 10 + 5 * this.text.scaleY
 this.add(this.roundedRect, this.text)
 }
}
export default Button

使用:

const button = new Button({
 width: 100,
 height: 40,
 text: 'Click Me!'
});

一般情况下,稍微复杂组合体都建议使用继承自 Group,这样利于扩展也方便管理自身内部的元件。

图片加载器

图片加载器返回 Promise

const { loadImage } = mprc;
// canvas参数为获取的canvas 2d对象实例
const imgObj = await loadImage('../logo.png', canvas);
// stage的图片加载方法
const stage = new Stage(canvas, 200, 200);
const imgObj2 = await stage.loadImage('../logo.png');
const bitmap = new bitMap(imgObj2);
stage.add(bitmap);
stage.updata();

注意事项

该项目参考了小程序、小游戏以及 Web 通用 Canvas 渲染引擎 Cax 和 spritejs。对 Cax 和 spritejs 开发者表示感谢。

  • Cax 是跨平台项目,但仅支持小程序旧版本 canvas(微信已放弃维护)。本项目仅支持微信小程序 canvas2d ,(基础库 2.9.0 以上版本)

  • 项目的一些方法和属性与 Cax 相似,但有些不同,使用时请注意区分

  • 该项目初始化传入参数为 canvas 对象而不是 ID,所以应获取 canvas 对象后初始化,具体请查看示例代码

  • 暂不支持的功能

    • 不支持 Fixed 属性
    • 不支持帧动画
    • 不支持 SVG Path 渲染
    • 不支持滤镜功能

项目轻量,使用简单,可用于对文字、图片、图形等绘制。适合海报、拼图、图表展示等项目开发。

对手势相关事件有良好的支持,内置拖拽功能,支持矩形边界和像素级边界两种选择方式。

项目对 canvas 的初始化采用显示设置宽高和通过缩放适应像素密度,显示高清。

对于较为复杂的项目,建议通过类开发组件,即每一组件是一个类,类中包含自己的布局和更新方法,这样可以开发出高度复用的组件,后续也便于维护。

To do

  • 帧动画

License

MIT

About

一个轻量级的微信小程序 Canvas (type="2d") 渲染引擎

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 66.6%
  • JavaScript 33.4%

AltStyle によって変換されたページ (->オリジナル) /