canvas小画板——(2)荧光笔效果
我们在上一篇文章中讲了如何绘制平滑曲线 canvas小画板——(1)平滑曲线。
透明度实现荧光笔
现在我们需要加另外一种画笔效果,带透明度的荧光笔。那可能会觉得绘制画笔的时候加上透明度就可以了。我们来在原来代码上设置

<!doctype html>
<html> <head>
<meta charset=utf-8>
<style>
canvas {
border: 1px solid #ccc
} body {
margin: 0;
}
</style>
</head> <body style="overflow: hidden;background-color: rgb(250, 250, 250);touch-action: none;">
<canvas id="c" width="1920" height="1080"></canvas>
<script>
var el = document.getElementById('c');
var ctx = el.getContext('2d');
//设置绘制线条样式
ctx.globalAlpha=0.3;
ctx.strokeStyle = 'red';
ctx.lineWidth = 10;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
var isDrawing;//标记是否要绘制
//存储坐标点
let points = [];
document.body.onpointerdown = function (e) {
console.log('pointerdown');
isDrawing = true;
points.push({ x: e.clientX, y: e.clientY });
};
document.body.onpointermove = function (e) {
console.log('pointermove');
if (isDrawing) {
draw(e.clientX, e.clientY);
} };
document.body.onpointerup = function (e) {
if (isDrawing) {
draw(e.clientX, e.clientY);
}
points = [];
isDrawing = false;
}; function draw(mousex, mousey) {
points.push({ x: mousex, y: mousey });
ctx.beginPath();
let x = (points[points.length - 2].x + points[points.length - 1].x) / 2,
y = (points[points.length - 2].y + points[points.length - 1].y) / 2;
if (points.length == 2) {
ctx.moveTo(points[points.length - 2].x, points[points.length - 2].y);
ctx.lineTo(x, y);
} else {
let lastX = (points[points.length - 3].x + points[points.length - 2].x) / 2,
lastY = (points[points.length - 3].y + points[points.length - 2].y) / 2;
ctx.moveTo(lastX, lastY);
ctx.quadraticCurveTo(points[points.length - 2].x, points[points.length - 2].y, x, y);
}
ctx.stroke();
points.slice(0, 1); }
</script>
</body> </html>
我们鼠标画线出来的效果如下,可以看到有很多重叠区域:

对canvas有所了解的同学,知道


解决荧光笔重叠问题
为什么会有这种重叠渲染颜色的问题呢?细细品味代码,你会发现是因为每次move的时候绘制的部分是上个鼠标点和当前鼠标点之前的连线,这样就会导致头部和尾部有重叠部分多次被stroke了。(不同连接设置的头部尾部重叠不同)
为了避免出现上述重叠这种问题下面介绍两种方法。
利用globalCompositeOperation
现在我们需要用上另外一个api方法
globalCompositeOperation,具体介绍可以看我另外一篇博文
Canvas学习:globalCompositeOperation详解,讲的比较详细。
这个小画板荧光笔效果我们需要使用globalCompositeOperation=‘xor’,另外注意透明度的设置不要使用context.
globalAlpha,在设置strokeStyle的时候用rgba设置透明度颜色。这个设置也是我不断尝试得出来的,具体为什么可以我也无法给出说法,有待研究或者知道的博友可以在评论给出答案。
<!doctype html>
<html> <head>
<meta charset=utf-8>
<style>
canvas {
border: 1px solid #ccc
} body {
margin: 0;
}
</style>
</head> <body style="overflow: hidden;background-color: rgb(250, 250, 250);touch-action: none;">
<canvas id="c" width="1920" height="1080"></canvas>
<script>
var el = document.getElementById('c');
var ctx = el.getContext('2d');
//设置绘制线条样式
ctx.strokeStyle = 'rgba(253, 58, 43, 0.5)';
ctx.lineWidth = 10;
ctx.lineJoin = 'round';
ctx.lineCap = 'round'; var isDrawing;//标记是否要绘制
//存储坐标点
let points = [];
document.body.onpointerdown = function (e) {
console.log('pointerdown');
isDrawing = true;
points.push({ x: e.clientX, y: e.clientY });
};
document.body.onpointermove = function (e) {
console.log('pointermove');
if (isDrawing) {
draw(e.clientX, e.clientY);
} };
document.body.onpointerup = function (e) {
if (isDrawing) {
draw(e.clientX, e.clientY);
}
points = [];
isDrawing = false;
}; function draw(mousex, mousey) {
points.push({ x: mousex, y: mousey });
ctx.globalCompositeOperation = "xor";//使用异或操作对源图像与目标图像进行组合。
ctx.beginPath();
let x = (points[points.length - 2].x + points[points.length - 1].x) / 2,
y = (points[points.length - 2].y + points[points.length - 1].y) / 2;
if (points.length == 2) {
ctx.moveTo(points[points.length - 2].x, points[points.length - 2].y);
ctx.lineTo(x, y);
} else {
let lastX = (points[points.length - 3].x + points[points.length - 2].x) / 2,
lastY = (points[points.length - 3].y + points[points.length - 2].y) / 2;
ctx.moveTo(lastX, lastY);
ctx.quadraticCurveTo(points[points.length - 2].x, points[points.length - 2].y, x, y);
}
ctx.stroke();
points.slice(0, 1); }
</script>
</body> </html>
存储坐标点
另有一种普遍做法是使用数组points存储每个点的坐标值,每次绘制前先清除画布内容,再循环points数组绘制路径,最后进行一次stroke。
这种方法每次只能保留一条线条,因为在不断的清除画布内容,如果需要保留住的话,可以扩展下points为二维数组,保留每一条线条的所有鼠标点。清除画布后遍历points数组重绘所有线条。
<!doctype html>
<html> <head>
<meta charset=utf-8>
<style>
canvas {
border: 1px solid #ccc
} body {
margin: 0;
}
</style>
</head> <body style="overflow: hidden;background-color: rgb(250, 250, 250);touch-action: none;">
<canvas id="c" width="1920" height="1080"></canvas>
<script>
var el = document.getElementById('c');
var ctx = el.getContext('2d');
//设置绘制线条样式
ctx.globalAlpha = 0.3;
ctx.strokeStyle = 'red';
ctx.lineWidth = 10;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
var isDrawing;//标记是否要绘制
//存储坐标点
let points = [];
document.body.onpointerdown = function (e) {
console.log('pointerdown');
isDrawing = true;
points.push({ x: e.clientX, y: e.clientY });
};
document.body.onpointermove = function (e) {
console.log('pointermove');
if (isDrawing) {
points.push({ x: e.clientX, y: e.clientY });
draw(e.clientX, e.clientY);
} };
document.body.onpointerup = function (e) {
if (isDrawing) {
points.push({ x: e.clientX, y: e.clientY });
draw(e.clientX, e.clientY);
}
points = [];
isDrawing = false;
}; function draw(mousex, mousey) {
ctx.clearRect(0, 0, 1920, 1080);
ctx.beginPath();
for (let i = 0; i < points.length; i++) {
if (i == 0)
ctx.moveTo(points[i].x, points[i].y);
else {
let p0 = points[i];
let p1 = points[i + 1];
let c, d;
if (!p1) {
c = p0.x;
d = p0.y;
}else {
c = (p0.x + p1.x) / 2;
d = (p0.y + p1.y) / 2;
}
ctx.quadraticCurveTo(p0.x, p0.y, c, d); //二次贝塞曲线函数
}
}
ctx.stroke();
}
</script>
</body> </html>
两种解决方法对比
这两种方法都可以实现荧光笔的效果,如下截图:

第一种方法只绘制上个点和当前点,而第二种需要绘制所有线条,所以从流畅性上对比第一种有优势。但如果需要实现橡皮擦的功能第一种就满足不了了,我的一篇博文中具体介绍了橡皮擦的实现可以参看
canvas小画板——(2)荧光笔效果的更多相关文章
- canvas小画板——(3)笔锋效果
画线准备 准备一个canvas <canvas id="canvasId" width="1000" height="800"> ...
- canvas小画板--(1)平滑曲线
功能需求 项目需求:需要实现一个可以自由书写的小画板 简单实现 对于熟悉canvas的同学来说,这个需求很简单,短短几十行代码就能实现: <!doctype html> <html& ...
- 如何开发一个简单的HTML5 Canvas 小游戏
原文:How to make a simple HTML5 Canvas game 想要快速上手HTML5 Canvas小游戏开发?下面通过一个例子来进行手把手教学.(如果你怀疑我的资历, A Wiz ...
- 两个Canvas小游戏
或许连小游戏都算不上,可以叫做mini游戏. 没有任何框架或者稍微有点深度的东西,所以有js基础的或者要追求炫酷效果的可以直接ctrl+w了. 先贴出两个游戏的试玩地址: 是男人就走30步 是男人就忍 ...
- 用HTML5 Canvas 做擦除及扩散效果
2013年的时候曾经使用canvas实现了一个擦除效果的需求,即模拟用户在模糊的玻璃上擦除水雾看到清晰景色的交互效果.好在2012年的时候学习HTML5的时候研究过canvas了,所以在比较短的时间内 ...
- canvas实现画板
canvas实现画板 主要用到知识点: 鼠标事件onmousedown() onmousemove() onmouseup() onmouseleave() 事件委托 canvas的一些方法 如:绘制 ...
- 浅谈canvas中的拖尾效果
引言 很早就想了解以下 canvas 中的拖尾效果(如彗星,烟花等效果)是怎么实现的,但是一直没有深入了解,正巧在 codepen 上看到一个 demo,代码简单,效果炫酷,故有此文. 什么黑科技 在 ...
- 基于canvas的原生JS时钟效果
概述 运用html5新增画布canvas技术,绘制时钟效果,无需引用任何插件,纯js. 详细 代码下载:http://www.demodashi.com/demo/11935.html 给大家介绍一个 ...
- 使用JavaScript和Canvas打造真实的雨滴效果
使用JavaScript和Canvas打造真实的雨滴效果 寸志 · 1 年前 我最近搞了一个有趣的项目——rainyday.js .我认为这个项目并不怎么样,而且,事实上这是我第一次尝试接触一些比弹窗 ...
随机推荐
- MYSQL 之 JDBC(十一): JDBC获取插入记录的主键值
取得数据库自动生成的主键值 package com.litian.jdbc; import javax.swing.plaf.nimbus.State; import java.sql.*; /** ...
- Integer和Long部分源码分析
Integer和Long的java中使用特别广泛,本人主要一下Integer.toString(int i)和Long.toString(long i)方法,其他方法都比较容易理解. Integer. ...
- 用matplotlib画简单折线图示例
例1 import numpy as np import matplotlib.pyplot as plt from scipy import stats rx1 = np.array([54.52, ...
- bzoj2056gift? 高精度?*
bzoj2056gift? 高精度? 题意: 给出abcdefghi,求2^a+2^b+2^c+2^d+2^e+2^f+2^g+2^h+i.a~h≤60,i≤2^63 题解: 发现只有极限数据才会爆u ...
- Oracle-常见的命令
--------------输入一下指令,按下快捷键 F8 select * from emp; --------------创建表格 create table 表名( 字段名1 数据类型1, 字段名 ...
- Apache Avro & Avro Schema简介
为什么需要schema registry? 首先我们知道: Kafka将字节作为输入并发布 没有数据验证 但是: 如果Producer发送了bad data怎么办? 如果字段被重命名怎么办? 如果数据 ...
- 重磅分享:美团点评架构师私藏的内部Linux运维笔记
最近不少小伙伴后台联系,希望能弄一些大厂的学习资料,我这边费了很大劲,联系到老朋友,原美团点评架构师张sir,问他要了些美团点评架构的内部资料. 这份资料含金量非常高,包含整个美团点评架构架构图,Li ...
- Makefile中的目标
Makefile中的目标 一般目标 目标就是我们需要的最终文件,也是make的最终输出 Makefile的运行机制是:先将目标当成文件,查看文件是否存在,如果存在且是最新,那么直接结束,如果文件不存在 ...
- .net core 自带分布式事务的微服务开源框架JMS
事务的统一性是微服务的一个重点问题,简洁有效的控制事务,更是程序员所需要的.JMS的诞生,就是为了更简单.更有效的控制事务. 先看一段调用微服务的代码: using (var ms = new JMS ...
- 2n皇后问题-------递归 暴力求解题与分布讨论题
问题描述 给定一个n*n的棋盘,棋盘中有一些位置不能放皇后.现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行.同一列或同一条对角线上,任意的两个白皇后都不在同一行.同一列或同一 ...