Paperjs

Paperjs 是一个较流行的 canvas 接口封装库,可以很方便的用来实现绘图功能,基本概念如下:

Geometry

Point, Size, Rectangle

Point:点,本质上是点的属性描述

1
2
3
var myPt = new Point(10, 20)  // 10,20 分别表示 x,y 坐标
// 除了用 X,Y 参数实例化一个点之外,还可以通过传入其他点作为参数来实例化(本质上是复制,彼此后续的改变是独立的,不会相互影响)
// 另外也可以调用旧点的 clone() 方法来实现复制

Size:尺寸,用来表示宽度和高度

1
2
var mySize = new Size(10, 20); // 表示宽度 width 为 10, 高度 height 为 20
console.log(mySize); // { width: 0, height: 0 }

Rectangle:矩形,有多种实例化的方法(这些方法让我发现,其背后的实现原理很可能是使用数组来存储参数)

1
2
3
4
5
6
7
8
9
10
11
var myPt = new Point(10, 20);
var mySz = new Size(100, 200);
var myRect = new Rectangle(myPt, mySz);

// 或者
var myRect = new Rectangle(10, 20, 100, 200);

// 或者
var myRect = new Rectangle();
myRect.point = new Point(10, 20);
myRect.size = new Size(100, 200);

Vector

矢量是一个非常好用的东西,原因:

  • 它不表示绝对坐标值,而是表示从起点到终点的相对坐标值;
  • 相对坐标的特性,让矢量可以很方便用来做各种计算;

两个矢量可以相加,也可以相减,在几何层面,它们其实仅是方向的区别;

矢量与整数的乘法或除法,也很简单,即相对坐标放大或缩小指定的整数倍数,或者也可以理解为在极坐标中,不改变角度,仅改变矢量长度;

1
var newVec = oldVec * 3 // 整数必须写在右边,因为 javascript 解释器默认取左边变量的类型作为计算结果的类型

除了乘法外,也可以通过改变矢量的 length,实现相同的效果

1
2
3
4
var newVec = oldVec * 3

// 跟下面的算法等价
newVec.length = oldVec.length * 3

矢量拥有角度 angle 属性,可以直接赋值,也可以对其进行计算

1
2
3
4
5
6
7
var vec = new Vector(100, 100);
console.log(vec.angle); // 45

// 直接赋值
vec.angle = 135;
// 或者
vec.angle = vec.angle + 90;

加减乘除、旋转等计算并不会改变旧的 vector 属性,而是会直接返回一个新的 vector;但当我们直接修改 vector 的属性时,则会改变 vector 的属性值;

Path

1
2
3
4
5
6
7
8
9
10
11
var myPath = new Path();

// 顺序添加新的点
myPath.add(new Point(0, 0));
myPath.add(new Point(100, 50));

// 支持一次添加多个点,只需传入多个参数即可
myPath.add(new Point(0, 0), new Point(100, 50));

// 支持在现有点之间插入新点
myPath.insert(1, new Point(30, 50));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// path 有一个 smooth 方法,可以用来将直线转成曲线
var path = new Path();
path.strokeColor = "black";
path.add(new Point(30, 75));
path.add(new Point(30, 25));
path.add(new Point(80, 25));
path.add(new Point(80, 75));
path.closed = true; // path 默认是 open 状态,设置为 true 实现闭合

path.fullySelected = true;

var copy = path.clone();
copy.fullySelected = true;
copy.position.x += 100;

copy.smooth();

// 自带的 remove 方法可以用来彻底删除对象
copy.remove();

1
2
3
4
5
6
7
8
9
10
11
12
// 创建 path 类型的圆
var circle = new Path.Circle(center_point, radius);

// 创建 path 类型的矩形
var path = new Path.Rectangle(point, size);
// 也可以传入 Rectangle 矩形作为实例化的参数
var rect = new Rectangle(new Point(50, 50), new Size(100, 100));
var path = new Path.Rectangle(rect);

// 创建圆角矩形
var radius = new Size(20, 20);
var path = new Path.Rectangle(rect, radius);

1
2
3
4
// 创建正多边形,例如正三角形,正十边形等
// new Path.RegularPolygon(center, sides, radius)
var triangle = new Path.RegularPolygon(new Point(80, 70), 3, 50);
var decagon = new Path.RegularPolygon(new Point(200, 70), 10, 50)

样式

1
2
3
4
5
// 创建一个打勾符号
var path = new Path({
segments: [[40, 115], [80, 180], [200, 20]],
selected: true
});

1
2
3
4
// 直接赋值,改成红色
path.strokeColor = "#ff0000";
// 或者使用 color 对象赋值
path.strokeColor = new Color(0.5, 0, 0.5);

1
2
// 填充颜色
path.fillColor = "#ff0000";

1
2
// 设置线段粗细
path.strokeWidth = 10;

1
2
// path 两端样式
path.storkeCap = "round";

1
2
// 中间点的样式设置为圆角
path.strokeJoin = "round";

1
2
// 虚线
path.dashArray = [10, 12];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// path 的所有相关样式都存在 style 属性中,用该字段对其他 path 进行赋值,可实现样式的复制
var firstPath = new Path.Circle({
center: [80, 50],
radius: 35
});

firstPath.strokeColor = '#ff0000';
firstPath.fillColor = 'blue';

// secondPath doesn't have a strokeColor yet:
var secondPath = new Path.Circle({
center: [160, 50],
radius: 35
});

// Apply the style of firstPath to that of secondPath:
secondPath.style = firstPath.style;

// style 也可以单独实例化,之后再赋值
var newStyle = {
strokeColor: "#ff0000",
fillColor: "#000000",
strokeWidth: 10,
}
path.style = newStyle;
1
2
3
4
5
// 删除某个样式,只需将该样式的属性值设置为 null 即可
path.fillColor = null;

// 如果要删除所有新式,则只需将整个 style 属性设置为 null 即可
path.style = null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 样式可以继承
project.currentStyle = {
strokeColor: "#000000"
}

// 新建的 path 会自动继承 project 的样式
var firstPath = new Path.circle({
center: [100, 100],
radius: 50,
});


// 当 project 的样式更新后,后续新创建的 path 会继承新样式
project.currentStyle.strokeWidth = 8;
project.currentStyle.fillColor = 'green';

var secondPath = new Path.Circle({
center: [250, 100],
radius: 50,
});

1
// path.simplify() 方法可用来简化组成 path 的 segment 数量,以便减少内存占用,提高性能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// path.flatten(error) 方法可用来将曲线转成多段直线
var path = new Path.Circle({
center: [80, 50],
radius: 35
});

// Select the path, so we can inspect its segments:
path.selected = true;

// Create a copy of the path and move it by 150 points:
var copy = path.clone();
copy.position.x += 150;

// Flatten the copied path, with a maximum error of 4 points:
copy.flatten(4);

交互

有三个全局的鼠标事件,可以对鼠标操作进行响应,它们分别是

  • onMouseDown
  • onMouseDrag
  • onMouse

鼠标事件的属性:

  • point:当前鼠标位置
  • downPoint:鼠标被按下时所在位置
  • lastPoint:上一次鼠标事件的位置
  • middlePoint:当前位置和上次位置的中点
  • delta:当前位置和上次位置的矢量 vector(up 事件该值为按下和松开两个位置的矢量)
1
2
3
4
5
6
7
8
9
10
11
12
13
// 每次按下鼠标时,就给路径添加一个新的点
var path = new Path();
path.strokeColor = "black";

function onMouseDown(e) {
path.add(e.point);
}

// 通过设置全局变量 tool,可控制鼠标的移动距离(最大、最小、固定距离等)
tool.minDistance = 20;
tool.maxDistance = 20;
tool.fixedDistance = 30;


Paperjs
https://ccw1078.github.io/2023/07/30/Paperjs/
作者
ccw
发布于
2023年7月30日
许可协议