我们在上一篇文章中讲了如何绘制平滑曲线 canvas小画板——(1)平滑曲线

透明度实现荧光笔

现在我们需要加另外一种画笔效果,带透明度的荧光笔。那可能会觉得绘制画笔的时候加上透明度就可以了。我们来在原来代码上设置

ctx.globalAlpha属性为0.3,或者将strokeStyle设置为rgba的形式如rgba(55,55,55,0.3),代码如下:

<!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有所了解的同学,知道

lineJoin和

lineCap的话可能会尝试改变这两个属性,实现之后也有同样重叠的效果。

解决荧光笔重叠问题

为什么会有这种重叠渲染颜色的问题呢?细细品味代码,你会发现是因为每次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画布内容--点擦除+线擦除

canvas小画板——(2)荧光笔效果的更多相关文章

  1. canvas小画板——(3)笔锋效果

    画线准备 准备一个canvas <canvas id="canvasId" width="1000" height="800"> ...

  2. canvas小画板--(1)平滑曲线

    功能需求 项目需求:需要实现一个可以自由书写的小画板 简单实现 对于熟悉canvas的同学来说,这个需求很简单,短短几十行代码就能实现: <!doctype html> <html& ...

  3. 如何开发一个简单的HTML5 Canvas 小游戏

    原文:How to make a simple HTML5 Canvas game 想要快速上手HTML5 Canvas小游戏开发?下面通过一个例子来进行手把手教学.(如果你怀疑我的资历, A Wiz ...

  4. 两个Canvas小游戏

    或许连小游戏都算不上,可以叫做mini游戏. 没有任何框架或者稍微有点深度的东西,所以有js基础的或者要追求炫酷效果的可以直接ctrl+w了. 先贴出两个游戏的试玩地址: 是男人就走30步 是男人就忍 ...

  5. 用HTML5 Canvas 做擦除及扩散效果

    2013年的时候曾经使用canvas实现了一个擦除效果的需求,即模拟用户在模糊的玻璃上擦除水雾看到清晰景色的交互效果.好在2012年的时候学习HTML5的时候研究过canvas了,所以在比较短的时间内 ...

  6. canvas实现画板

    canvas实现画板 主要用到知识点: 鼠标事件onmousedown() onmousemove() onmouseup() onmouseleave() 事件委托 canvas的一些方法 如:绘制 ...

  7. 浅谈canvas中的拖尾效果

    引言 很早就想了解以下 canvas 中的拖尾效果(如彗星,烟花等效果)是怎么实现的,但是一直没有深入了解,正巧在 codepen 上看到一个 demo,代码简单,效果炫酷,故有此文. 什么黑科技 在 ...

  8. 基于canvas的原生JS时钟效果

    概述 运用html5新增画布canvas技术,绘制时钟效果,无需引用任何插件,纯js. 详细 代码下载:http://www.demodashi.com/demo/11935.html 给大家介绍一个 ...

  9. 使用JavaScript和Canvas打造真实的雨滴效果

    使用JavaScript和Canvas打造真实的雨滴效果 寸志 · 1 年前 我最近搞了一个有趣的项目——rainyday.js .我认为这个项目并不怎么样,而且,事实上这是我第一次尝试接触一些比弹窗 ...

随机推荐

  1. MYSQL 之 JDBC(十一): JDBC获取插入记录的主键值

    取得数据库自动生成的主键值 package com.litian.jdbc; import javax.swing.plaf.nimbus.State; import java.sql.*; /** ...

  2. Integer和Long部分源码分析

    Integer和Long的java中使用特别广泛,本人主要一下Integer.toString(int i)和Long.toString(long i)方法,其他方法都比较容易理解. Integer. ...

  3. 用matplotlib画简单折线图示例

    例1 import numpy as np import matplotlib.pyplot as plt from scipy import stats rx1 = np.array([54.52, ...

  4. 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 ...

  5. Oracle-常见的命令

    --------------输入一下指令,按下快捷键 F8 select * from emp; --------------创建表格 create table 表名( 字段名1 数据类型1, 字段名 ...

  6. Apache Avro & Avro Schema简介

    为什么需要schema registry? 首先我们知道: Kafka将字节作为输入并发布 没有数据验证 但是: 如果Producer发送了bad data怎么办? 如果字段被重命名怎么办? 如果数据 ...

  7. 重磅分享:美团点评架构师私藏的内部Linux运维笔记

    最近不少小伙伴后台联系,希望能弄一些大厂的学习资料,我这边费了很大劲,联系到老朋友,原美团点评架构师张sir,问他要了些美团点评架构的内部资料. 这份资料含金量非常高,包含整个美团点评架构架构图,Li ...

  8. Makefile中的目标

    Makefile中的目标 一般目标 目标就是我们需要的最终文件,也是make的最终输出 Makefile的运行机制是:先将目标当成文件,查看文件是否存在,如果存在且是最新,那么直接结束,如果文件不存在 ...

  9. .net core 自带分布式事务的微服务开源框架JMS

    事务的统一性是微服务的一个重点问题,简洁有效的控制事务,更是程序员所需要的.JMS的诞生,就是为了更简单.更有效的控制事务. 先看一段调用微服务的代码: using (var ms = new JMS ...

  10. 2n皇后问题-------递归 暴力求解题与分布讨论题

    问题描述 给定一个n*n的棋盘,棋盘中有一些位置不能放皇后.现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行.同一列或同一条对角线上,任意的两个白皇后都不在同一行.同一列或同一 ...