├── .npmignore
├── .gitignore
├── html
├── clock.html
├── cursor.html
├── sudoku.html
├── coderain.html
├── flydots.html
├── circletext.html
├── the_last_janvas.html
├── taichi.html
├── about_edge.html
├── about_wheel.html
├── beziermaker.html
├── stats.html
├── about_antv_performance_test.html
├── hello_world.html
└── img
│ └── complex.svg
├── package.json
├── LICENSE
├── js
├── circletext.js
├── coordinate.js
├── aboutedge.js
├── aboutwheel.js
├── cursor.js
├── flydots.js
├── stats.js
├── taichi.js
├── clock.js
├── coderain.js
├── antv.js
├── beziermaker.js
├── thelastjanvas.js
└── sudoku.js
├── README.md
└── dist
└── janvasexamples.min.js
/.npmignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /node_modules
3 |
4 |
--------------------------------------------------------------------------------
/html/clock.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Clock
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/html/cursor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cursor
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/html/sudoku.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Sudoku
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/html/coderain.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CodeRain
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/html/flydots.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | FlyDots
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/html/circletext.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CircleText
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/html/the_last_janvas.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | TheLastJanvas
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/html/taichi.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | TaiChi
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/html/about_edge.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | About Edge
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/html/about_wheel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | About Wheel
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/html/beziermaker.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | BezierMaker
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "janvasexamples",
3 | "version": "1.4.0",
4 | "description": "Examples created with janvas.",
5 | "main": "dist/janvasexamples.min.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/JarenChow/JanvasExamples.git"
12 | },
13 | "keywords": [
14 | "janvas",
15 | "examples",
16 | "javascript",
17 | "canvas",
18 | "svg",
19 | "2d"
20 | ],
21 | "author": "JarenChow",
22 | "license": "MIT",
23 | "dependencies": {
24 | "janvas": "2.9.7"
25 | },
26 | "bugs": {
27 | "url": "https://github.com/JarenChow/JanvasExamples/issues"
28 | },
29 | "homepage": "https://github.com/JarenChow/JanvasExamples#readme"
30 | }
31 |
--------------------------------------------------------------------------------
/html/stats.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Stats
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 JarenChow
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/html/about_antv_performance_test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | AboutAntVPerformanceTest
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/js/circletext.js:
--------------------------------------------------------------------------------
1 | var circleText = new janvas.Canvas({
2 | container: "#app",
3 | duration: 2000,
4 | props: {
5 | back: void (0),
6 | str: "HELLOJANVAS",
7 | texts: [],
8 | length: 0,
9 | ease: janvas.Utils.ease.inout.bounce
10 | },
11 | methods: {
12 | init: function () {
13 | this.back = new janvas.Rect(this.$ctx, 0, 0);
14 | this.back.getStyle().setFillStyle("gold");
15 | this.setText(this.str);
16 | },
17 | resize: function () {
18 | var w = this.$width, h = this.$height;
19 | this.back.setWidth(w).setHeight(h);
20 | for (var i = 0; i < this.length; i++) {
21 | this.texts[i].init(w / 2, h / 2 - Math.min(w, h) * 0.309, w / 2, h / 2);
22 | }
23 | },
24 | setText: function (textString) {
25 | var w = this.$width, h = this.$height;
26 | this.length = textString.length;
27 | while (this.texts.length < this.length) {
28 | var text = new janvas.Text(this.$ctx).init(w / 2, h / 2 - Math.min(w, h) * 0.309, w / 2, h / 2);
29 | text.getStyle().setFont("small-caps bold 8em courier")
30 | .setTextAlign("center").setTextBaseline("middle");
31 | this.texts.push(text);
32 | }
33 | for (var i = 0; i < this.length; i++) {
34 | this.texts[i].setText(textString[i]).targetAngle = Math.PI * 2 * i / this.length;
35 | }
36 | this.$raf.start();
37 | },
38 | update: function (ts) {
39 | for (var i = 0; i < this.length; i++) {
40 | var text = this.texts[i];
41 | text.getMatrix().setAngle(text.targetAngle * this.ease(ts / this.$duration));
42 | }
43 | },
44 | draw: function () {
45 | this.back.fill();
46 | for (var i = 0; i < this.length; i++) this.texts[i].fill();
47 | }
48 | },
49 | events: {
50 | keydown: function (ev) {
51 | var key = ev.key;
52 | if (key === "Backspace") this.str = this.str.substring(0, this.str.length - 1);
53 | else if (key.length === 1) this.str += key.toUpperCase();
54 | this.setText(this.str);
55 | }
56 | }
57 | });
58 |
--------------------------------------------------------------------------------
/html/hello_world.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Hello World
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/js/coordinate.js:
--------------------------------------------------------------------------------
1 | var coordinate = new janvas.Canvas({
2 | container: "#app",
3 | props: {
4 | _backgroundColor: "rgb(237, 237, 237)",
5 | _font: "12px Consolas",
6 | _span: 50,
7 | _dash: [10, 5],
8 | _dashColor: "rgba(0, 0, 0, 0.2)"
9 | },
10 | methods: {
11 | init: function () {
12 | this.background = new janvas.Rect(this.$ctx, 0, 0, 0, 0);
13 | this.xAxis = new janvas.Line(this.$ctx, 0, 0, 0, 0);
14 | this.yAxis = new janvas.Line(this.$ctx, 0, 0, 0, 0);
15 | this.xLines = [];
16 | this.xTexts = [];
17 | this.yLines = [];
18 | this.yTexts = [];
19 | this.oText = new janvas.Text(this.$ctx, 0, 0, "0");
20 | },
21 | draw: function () {
22 | this.background.fill();
23 | this.xAxis.stroke();
24 | this.yAxis.stroke();
25 | this.xLines.forEach(function (line) {
26 | line.stroke();
27 | });
28 | this.yLines.forEach(function (line) {
29 | line.stroke();
30 | });
31 | this.xTexts.forEach(function (text) {
32 | text.fill();
33 | });
34 | this.yTexts.forEach(function (text) {
35 | text.fill();
36 | });
37 | this.oText.fill();
38 | }
39 | },
40 | events: {
41 | resize: function () {
42 | this.background.setWidth(this.$width).setHeight(this.$height);
43 | this.xAxis.setEnd(this.$width, 0);
44 | this.yAxis.setEnd(0, this.$height);
45 | this.adjustLength(Math.floor(this.$width / this._span - 0.2), this.xTexts, this.xLines, true);
46 | this.adjustLength(Math.floor(this.$height / this._span - 0.2), this.yTexts, this.yLines, false);
47 | this.setStyles();
48 | }
49 | },
50 | functions: {
51 | setStyles: function () {
52 | this.background.getStyle().setFillStyle(this._backgroundColor);
53 | this.oText.getStyle().setFont(this._font).setTextAlign("left").setTextBaseline("top");
54 | this.xLines.forEach(function (line) {
55 | line.getStyle().setStrokeStyle(this._dashColor).setLineDash(this._dash);
56 | }, this);
57 | this.yLines.forEach(function (line) {
58 | line.getStyle().setStrokeStyle(this._dashColor).setLineDash(this._dash);
59 | }, this);
60 | this.xTexts.forEach(function (text) {
61 | text.getStyle().setFont(this._font).setTextAlign("center").setTextBaseline("top");
62 | }, this);
63 | this.yTexts.forEach(function (text) {
64 | text.getStyle().setFont(this._font).setTextAlign("left").setTextBaseline("middle");
65 | }, this);
66 | },
67 | adjustLength: function (count, texts, lines, inAxisX) {
68 | var len, pos;
69 | while ((len = lines.length) < count) {
70 | pos = (len + 1) * this._span;
71 | if (inAxisX) {
72 | texts.push(new janvas.Text(this.$ctx, pos, 0, pos + ""));
73 | lines.push(new janvas.Line(this.$ctx, pos, 0, pos, 0));
74 | } else {
75 | texts.push(new janvas.Text(this.$ctx, 0, pos, pos + ""));
76 | lines.push(new janvas.Line(this.$ctx, 0, pos, 0, pos));
77 | }
78 | }
79 | if (count >= 0) texts.length = lines.length = count;
80 | lines.forEach(function (line) {
81 | inAxisX ? line.setEndY(this.$height) : line.setEndX(this.$width);
82 | }, this);
83 | }
84 | }
85 | });
86 |
--------------------------------------------------------------------------------
/js/aboutedge.js:
--------------------------------------------------------------------------------
1 | var aboutEdge = new janvas.Canvas({
2 | container: "#app",
3 | props: {
4 | points: [],
5 | current: void (0)
6 | },
7 | methods: {
8 | init: function () {
9 | this.background = new janvas.Rect(this.$ctx, 0, 0, this.$width, this.$height);
10 | this.start = new janvas.Arc(this.$ctx, 200, 250, 5);
11 | this.start.getStyle().setFillStyle("hsl(0, 80%, 60%)");
12 | this.end = new janvas.Arc(this.$ctx, 500, 250, 5);
13 | this.end.getStyle().setFillStyle("hsl(90, 80%, 60%)");
14 | this.an = new janvas.Arc(this.$ctx, 525, 300, 5);
15 | this.an.getStyle().setFillStyle("hsl(180, 80%, 60%)");
16 | this.points.push(this.start, this.end, this.an);
17 | this.edge = new janvas.Edge(this.$ctx);
18 | this.text = new janvas.Text(this.$ctx, 0, 0, "Janvas");
19 | this.text.getStyle().setFont("12px sans-serif").setTextAlign("center")
20 | .setTextBaseline("middle");
21 | this.edge.setEmptyLength(janvas.Utils.measureTextWidth(this.text.getText(),
22 | this.text.getStyle().getFont()) / 0.809);
23 | this.head = new janvas.Triangle(this.$ctx).setLength(12);
24 | this.head.getStyle().setFillStyle("hsl(270, 80%, 60%)");
25 | this.setCurvePropsAndDraw();
26 | },
27 | draw: function () {
28 | this.$clear();
29 | this.edge.stroke();
30 | if (this.edge.ratioInRange()) {
31 | var an = this.edge.getAngle();
32 | this.text.getMatrix().setAngle(an > -Math.PI / 2 && an < Math.PI / 2 ? an : an + Math.PI);
33 | this.text.init(this.edge.getTargetX(), this.edge.getTargetY(),
34 | this.edge.getTargetX(), this.edge.getTargetY()).fill();
35 | }
36 | this.start.fill();
37 | this.end.fill();
38 | this.an.fill();
39 | this.head.setRotation(this.edge.getAnchorAngle()).fill();
40 | }
41 | },
42 | events: {
43 | mousedown: function () {
44 | this._mousedown = true;
45 | this.points.forEach(function (point) {
46 | point.lastX = point.getStartX();
47 | point.lastY = point.getStartY();
48 | });
49 | },
50 | mousemove: function (ev) {
51 | if (this._mousedown) {
52 | if (ev.buttons === 2) {
53 | this.points.forEach(function (point) {
54 | point.setStart(point.lastX + ev.$moveX, point.lastY + ev.$moveY);
55 | }, this);
56 | this.setCurvePropsAndDraw();
57 | } else {
58 | if (!this.current) return;
59 | this.current.setStart(this.current.lastX + ev.$moveX,
60 | this.current.lastY + ev.$moveY);
61 | this.setCurvePropsAndDraw();
62 | }
63 | } else {
64 | this.current = void (0);
65 | this.points.forEach(function (point) {
66 | if (point.isPointInPath(ev.$x, ev.$y)) {
67 | this.current = point;
68 | this.setCursor("pointer");
69 | }
70 | }, this);
71 | if (!this.current) this.setCursor("");
72 | }
73 | },
74 | mouseup: function () {
75 | this._mousedown = false;
76 | }
77 | },
78 | functions: {
79 | setCurvePropsAndDraw: function () {
80 | this.edge.setStart(this.start.getStartX(), this.start.getStartY())
81 | .setEnd(this.end.getStartX(), this.end.getStartY())
82 | .setAnchor(this.an.getStartX(), this.an.getStartY());
83 | this.head.setStart(this.end.getStartX(), this.end.getStartY());
84 | this.draw();
85 | },
86 | setCursor: function (cursor) {
87 | if (this.$canvas.style.cursor !== cursor) this.$canvas.style.cursor = cursor;
88 | }
89 | }
90 | });
91 |
--------------------------------------------------------------------------------
/html/img/complex.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/js/aboutwheel.js:
--------------------------------------------------------------------------------
1 | var aboutWheel = new janvas.Canvas({
2 | container: "#app",
3 | duration: Infinity,
4 | interval: 16,
5 | props: {
6 | size: 50
7 | },
8 | methods: {
9 | init: function () {
10 | this.background = new janvas.Rect(this.$ctx, 0, 0, this.$width, this.$height);
11 | var cx = this.$width / 2, cy = this.$height / 2;
12 | this.img = new janvas.Image(this.$ctx, cx - this.size, cy - this.size,
13 | "img/complex.svg", cx, cy, this.size * 2, this.size * 2);
14 | this.img.getStyle().setStrokeStyle("grey");
15 | this.img.animationQueue = []; // 动画队列
16 | this.img.count = this.img.maxCount = Math.floor(256 / this.$interval);
17 | this.$raf.resume();
18 | },
19 | update: function (ts) {
20 | this.img.getMatrix().setAngle(Math.PI / 2000 * ts);
21 | this.scaleAnimation();
22 | },
23 | draw: function () {
24 | this.$clear();
25 | this.img.draw();
26 | if (this.img._mousein) this.img.stroke();
27 | }
28 | },
29 | events: {
30 | mousedown: function () {
31 | if (this.img._mousein) {
32 | this._mousedown = true;
33 | this.img.lastX = this.img.getStartX();
34 | this.img.lastY = this.img.getStartY();
35 | }
36 | },
37 | mousemove: function (ev) {
38 | if (this._mousedown) {
39 | var mx = this.img.lastX + ev.$moveX, my = this.img.lastY + ev.$moveY;
40 | this.img.init(mx, my, mx + this.size, my + this.size);
41 | } else {
42 | this.img._mousein = this.img.isPointInPath(ev.$x, ev.$y);
43 | }
44 | },
45 | mouseup: function () {
46 | this._mousedown = false;
47 | },
48 | wheel: function (ev) {
49 | /**
50 | * 当我们缩放一个对象的时候,有两种情况
51 | * 1. 中心点为 0, 0
52 | * 这种情况只能去缩放对象的 startX, startY
53 | * 2. 中心点不为 0, 0
54 | * 这种情况可选择缩放对象的 startX, startY
55 | * 因为存在中心点,所以在缩放后,需校准一次 offset,值为 _sx|_sy*(1-scale)
56 | * 也可以优先选择缩放对象的 originX, originY
57 | * 这样子就无需校准对象的 offset
58 | * 以上为笛卡尔坐标系内对象的缩放内容,当存在中心点时优先缩放中心点而不是起始绘制点
59 | * 其实就是一个比例问题(以下内容:target为目标点,event为事件点,point为对象点)
60 | * 缩放:(target-event)/scale=(point-event)/lastScale
61 | * 偏移:_sx|sy/1 = offset/(scale-1) // 存在中心点且缩放起始绘制点导致缩放图形而产生的偏移量
62 | */
63 | // 方式一(不推荐):
64 | /*var targetSx = this.x + (this.img.getStartX() - this.x) * this.scaling,
65 | targetSy = this.y + (this.img.getStartY() - this.y) * this.scaling;
66 | this.img.init(targetSx, targetSy, targetSx + this.size, targetSy + this.size);
67 | this.img.getMatrix().setScale(this.scale, this.scale).setOffset(
68 | this.img._sx * (1 - this.scale), // 校正偏移量,不推荐此做法
69 | this.img._sy * (1 - this.scale)
70 | );*/
71 | // 方式二(推荐):
72 | // var targetCx = ev.$x + (this.img.getOriginX() - ev.$x) * ev.$scaling,
73 | // targetCy = ev.$y + (this.img.getOriginY() - ev.$y) * ev.$scaling;
74 | // this.img.init(targetCx - this.size, targetCy - this.size, targetCx, targetCy)
75 | // .getMatrix().setScale(ev.$scale, ev.$scale);
76 | // 方式三:方式二的简单动画版本,有需求时有必要写进 components 里进行实现,以便解耦
77 | ev.preventDefault();
78 | if (ev.$scaling !== 1) this.img.animationQueue.unshift(ev);
79 | }
80 | },
81 | functions: {
82 | scaleAnimation: function () {
83 | if (this.img.animationQueue.length && this.img.count === this.img.maxCount) {
84 | var ev = this.img.animationQueue.pop();
85 | this.img.lastCx = this.img.getOriginX();
86 | this.img.lastCy = this.img.getOriginY();
87 | this.img.targetCx = ev.$x + (this.img.getOriginX() - ev.$x) * ev.$scaling;
88 | this.img.targetCy = ev.$y + (this.img.getOriginY() - ev.$y) * ev.$scaling;
89 | this.img.lastScale = ev.$lastScale;
90 | this.img.targetScale = ev.$scale;
91 | this.img.count = 0;
92 | }
93 | if (this.img.count < this.img.maxCount) {
94 | this.img.count++;
95 | var ratio = this.img.count / this.img.maxCount,
96 | stampCx = this.img.lastCx + (this.img.targetCx - this.img.lastCx) * ratio,
97 | stampCy = this.img.lastCy + (this.img.targetCy - this.img.lastCy) * ratio,
98 | scale = this.img.lastScale + (this.img.targetScale - this.img.lastScale) * ratio;
99 | this.img.init(stampCx - this.size, stampCy - this.size, stampCx, stampCy)
100 | .getMatrix().setScale(scale, scale);
101 | }
102 | }
103 | }
104 | });
105 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JanvasExamples
2 |
3 | Examples created with [janvas](https://github.com/jarenchow/janvas).
4 |
5 | ## 使用示例
6 |
7 | - 方式一
8 | 1. 准备一个容器 div:``
9 | 2. 添加 janvas 库:``
10 | 3. 添加 janvasexamples 库:``
11 | 4. 填充 div:`var fly = janvasexamples.flydots("#app")`
12 | - 方式二(在 [vue](https://github.com/vuejs/vue) 中使用)
13 | 1. 准备一个容器 div:``
14 | 2. `npm install janvasexamples --save`
15 | 3. 填充 div:`var fly = janvasexamples.flydots(this.$refs.container);`
16 |
17 | ## [About AntV Performance Test](https://jarenchow.github.io/JanvasExamples/html/about_antv_performance_test.html)
18 |
19 | 原示例:[https://g6.antv.vision/zh/examples/performance/perf#moreData](https://g6.antv.vision/zh/examples/performance/perf#moreData),如其所说:
20 |
21 | > 对 G6 的性能测试,用来验证 G6 能够承载的数据量,分别使用 5000+ 图元、将近 20000 图元及 50000+ 图元的示例进行了测试,从结果来看,20000 左右图元时,G6 是可以正常交互的,当数据量达到 50000+ 时,交互就会出现一定的卡顿,但对于绝大部分业务来说,都不建议在画布上展示如此多的数据,具体的做法可以参考 AntV G6 团队的大图可视化方案,预计 1122 发布
22 |
23 | 而使用 **janvas** 从低抽象角度来定制,数据量即使达到 **50000\+** 时,依然可以**缩放**、**拖曳**以及**自定义更多交互**。
24 |
25 | ## [CodeRain](https://jarenchow.github.io/JanvasExamples/html/coderain.html)
26 |
27 | 基于 **janvas** 编写不到 *200* 行代码几乎 100% 还原 The Matrix 特效代码雨。
28 |
29 | 可以使用 `janvasexamples.coderain(document.body).$canvas.style.zIndex = "-1";` 来为自己的网页添加此特效。
30 |
31 | ## [TheLastJanvas](https://jarenchow.github.io/JanvasExamples/html/the_last_janvas.html)
32 |
33 | 在 **CodePen** 上非常出名的 [The Last Experience](https://codepen.io/ge1doot/pen/LkdOwj),使用 **janvas** 改写的示例。
34 |
35 | 原作者 [ge1doot](https://codepen.io/ge1doot) 使用了 `