canvas小画板--(1)平滑曲线
功能需求
项目需求:需要实现一个可以自由书写的小画板
简单实现
对于熟悉canvas的同学来说,这个需求很简单,短短几十行代码就能实现:
<!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 = 'red';
ctx.lineWidth = 1;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
var isDrawing;//标记是否要绘制
//存储坐标点
let lastX, lastY;
document.body.onpointerdown = function (e) {
console.log('pointerdown');
isDrawing = true;
lastX = e.clientX;
lastY = e.clientY;
};
document.body.onpointermove = function (e) {
console.log('pointermove');
if (isDrawing) {
draw(e.clientX, e.clientY, lastX, lastY);
}
lastX = e.clientX, lastY = e.clientY;
};
document.body.onpointerup = function (e) {
if (isDrawing) {
draw(e.clientX, e.clientY, lastX, lastY);
}
lastX = e.clientX, lastY = e.clientY;
isDrawing = false;
}; function draw(x, y, lastX, lastY) {
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(x, y);
ctx.stroke();
}
</script>
</body>
</html>
实现效果如下图:

实现的逻辑很简单:
1)监听事件pointerdown,pointermove,pointerup
2)标记是否拖拽画线模式变量 isDrawing,在down事件时置为true,up的时候置为false
3)使用canvas的api,设置线条样式,调用绘制线条接口lineTo方法
以上就简单的实现了画板功能,如果要求不高的用户可以使用,但一旦遇到有点要求的用户就无法交付这种产品,仔细看是线条折线感太强。
为什么会有折线感呢?
主要原因:
我们调用的api方法lineTo是两点连线也就是直线
浏览器对鼠标事件mousemove的采集是有采集频率的,并不是每个鼠标移动经过的每一个像素点都会触发事件。
当鼠标移动的越快,那么两点之间的间隔就越远,那么折线感就更明显。

如何能绘制平滑的曲线?
canvas提供的api中是有现成接口的,贝塞尔系列的接口就能满足我们的要求,接下来我们讲一下使用二次贝塞尔曲线绘制平滑曲线。
quadraticCurveTo(cpx,cpy,x,y)
二次贝塞尔曲线接口需要四个参数,cpx,cpy是曲线的控制点,x,y是曲线终点。
有人问那曲线的起点在哪里?其实曲线的起点取决于上一操作状态,可以是moveTo的位置,或者是lineTo的位置,或者是贝塞尔的终点。
那么怎么调用quadraticCurveTo,参数怎么传呢?
我们需要找出关键位置,直接用例子告诉大家吧
1)假如我们用鼠标采集到ABCDEF六个点
2)取前面三个点ABC计算,BC的中点B1,以A为起点,B为控制点,B1为终点,那么利用quadraticCurveTo可以绘制出这样一条贝塞尔曲线

3)接下来计算CD的中点C1,以B1为起点,C为控制点,C1为终点,那么利用quadraticCurveTo可以绘制出这样一条贝塞尔曲线

4)以此类推,当到了最后一个点时以D1为起点,E为控制点,F为终点,结束贝塞尔绘制。

根据算法进行代码改造
OK我们介绍了具体算法的影响,那用该算法对我们前面的代码进行改造:
<!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 = 'red';
ctx.lineWidth = 1;
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>
在原有基础上我们用了一个数组points保存鼠标经过的点,根据算法可知绘制贝塞尔曲线至少要用三个点,绘制过程中维护points数组。
实现效果如下,可见平滑了很多!

canvas小画板--(1)平滑曲线的更多相关文章
- canvas小画板——(2)荧光笔效果
我们在上一篇文章中讲了如何绘制平滑曲线 canvas小画板——(1)平滑曲线. 透明度实现荧光笔 现在我们需要加另外一种画笔效果,带透明度的荧光笔.那可能会觉得绘制画笔的时候加上透明度就可以了.我们来 ...
- canvas小画板——(3)笔锋效果
画线准备 准备一个canvas <canvas id="canvasId" width="1000" height="800"> ...
- 如何开发一个简单的HTML5 Canvas 小游戏
原文:How to make a simple HTML5 Canvas game 想要快速上手HTML5 Canvas小游戏开发?下面通过一个例子来进行手把手教学.(如果你怀疑我的资历, A Wiz ...
- 两个Canvas小游戏
或许连小游戏都算不上,可以叫做mini游戏. 没有任何框架或者稍微有点深度的东西,所以有js基础的或者要追求炫酷效果的可以直接ctrl+w了. 先贴出两个游戏的试玩地址: 是男人就走30步 是男人就忍 ...
- canvas实现画板
canvas实现画板 主要用到知识点: 鼠标事件onmousedown() onmousemove() onmouseup() onmouseleave() 事件委托 canvas的一些方法 如:绘制 ...
- 一款基于HTML5 Canvas的画板涂鸦动画
今天给各网友分享一款基于HTML5 Canvas的画板涂鸦动画.记得之前我们分享过一款HTML5 Canvas画板工具,可以切换不同的笔刷,功能十分强大.本文今天要再来分享一款基于HTML5 Canv ...
- [原创]基于html5新标签canvas写的一个小画板
最近刚学了canvas,写个小应用练习下 源代码 <!DOCTYPE> <html> <head> <meta http-equiv="Conten ...
- 用canvas画一个的小画板(PC端移动端都能用)
前言 本篇的内容主要包括: canvas标签简介 画板的功能简介 画板的JS部分(包括:1.获取画布 2.使画板全屏幕显示且自适应 3.如何绘制直线 4.绘画时的三种状态(鼠标点击.移动.离开)5.画 ...
- HTML5 Canvas 小例子 简易画板
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
随机推荐
- mui div滚动阻止触发下拉刷新
function orderListScroll () { var _orderObj = document.querySelector('.circulation-loan-list') //div ...
- (一)pandas的两种对象
将鱼图像数据进行操作,使用numpy知识 import numpy as np import matplotlib.pyplot as plt %matplotlib inline #咱们可以不用sh ...
- 爬虫05 /js加密/js逆向、常用抓包工具、移动端数据爬取
爬虫05 /js加密/js逆向.常用抓包工具.移动端数据爬取 目录 爬虫05 /js加密/js逆向.常用抓包工具.移动端数据爬取 1. js加密.js逆向:案例1 2. js加密.js逆向:案例2 3 ...
- scrapy 源码解析 (五):启动流程源码分析(五) Scraper刮取器
Scraper刮取器 对ExecutionEngine执行引擎篇出现的Scraper进行展开.Scraper的主要作用是对spider中间件进行管理,通过中间件完成请求.响应.数据分析等工作. Scr ...
- Unity-JobSystom
什么是Job System? 一个job system通过创建jobs而不是线程来管理多线程的代码.Job是一个小的工作单元,不等同线程.管理运行在多个核心上的一组工人线程(worker thread ...
- day2:Number,tuple,str,list,set,dict
# ### Number ( int float bool complex) 1.int 整型 (正整数 0 负整数)intvar = 1print(intvar) # type 获取值的类型res ...
- JavaScript图形实例:平面镶嵌图案
用形状.大小完全相同的一种或几种平面图形进行拼接,彼此之间不留空隙.不重叠地铺成一片,就叫做这几种图形的平面镶嵌. 1.用一种多边形实现的平面镶嵌图案 我们可以采用正三角形.正方形或正六边形实现平面镶 ...
- bzoj3437小P的牧场
bzoj3437小P的牧场 题意: n个牧场,在每个牧场见控制站的花费为ai,在该处建控制站能控制从此处到左边第一个控制站(或边界)之间的牧场.一个牧场被控制的花费等于它到控制它的控制站之间的牧场数目 ...
- Python实现性能自动化测试竟然如此简单【颠覆你的三观】
一.思考 1.什么是性能自动化测试? 性能系统负载能力 超负荷运行下的稳定性 系统瓶颈 自动化测试使用程序代替手工 提升测试效率 性能自动化使用代码模拟大批量用户 让用户并发请求 多页面多用户并发 ...
- C++ 简单介绍线段树
题目描述 如题,已知一个数列,你需要进行下面两种操作: 将某区间每一个数加上k. 求出某区间每一个数的和. 输入格式 第一行包含两个整数n,m分别表示该数列数字的个数和操作的总个数. 第二行包含n个用 ...