Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >利用canvas实现毛笔字帖(一)

利用canvas实现毛笔字帖(一)

作者头像
用户1394570
发布于 2018-08-02 03:26:45
发布于 2018-08-02 03:26:45
2.7K02
代码可运行
举报
文章被收录于专栏:carvencarven
运行总次数:2
代码可运行

最近在慕课网上找到了很好的canvas教程, 来自 @liuyubobobo学写一个字 canvas绘图教程 在 @liuyubobobo 老师的系列canvas教程中,我学到不少知识。 今天,运用在视频中的所学,结合自己的代码风格,我自己尝试也写一个字帖出来,在这里分享一下思路和过程 具体代码其实已经push在github上,感兴趣的可以clone下来参考一下。 代码/canvas-demo/write 这里还有在线的效果演示 在线演示

想法

在canvas在实现这样一个效果,有一个米字格,可以用鼠标(pc)甚至手指(手机)在上面写字,字要有点像毛笔字。 下面有控制部件,可以控制笔的颜色,还是可以清空米字格的墨迹。

下面就开始着手去写了。

代码

css和html部分不是很多,也不是我要讲的重点,简单贴出来

html

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>毛笔字</title>
    <meta name="viewport" content="width=device-width;height=device-height;initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <link href="css/write.css" rel="stylesheet" type="text/css">
</head>
<body>
<canvas id="canvas">
    您的浏览器版本不支持canvas,请更新或者下载chrome
</canvas>
<div id="controller">
    <div id="black" class="on"></div>
    <div id="red"></div>
    <div id="green"></div>
    <div id="yellow"></div>
    <div id="purple"></div>
    <div id="orange"></div>
    <div id="blue"></div>
    <div id="indigo"></div>
    <button id="reset">清除</button>
</div>
</body>
</html>

css

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#canvas{
    display: block;  margin: 20px auto;  border: #ff1722 5px solid;
}
/*控制器*/
#controller{
    text-align: center;
}
#controller>div{
    display: inline-block;  width: 30px;  height: 30px;  border: gray 2px solid;
}
#black{
    background: black;
}
#red{
    background: red;
}
#green{
    background: green;
}
#yellow{
    background: yellow;
}
#purple{
    background: purple;
}
#orange{
    background: orange;
}
#blue{
    background: blue;
}
#indigo{
    background: indigo;
}
#controller>div.on{
    border-color: red;
}
#reset{
    display: inline-block;  vertical-align: top;  width: 50px;  height: 34px;
}

javascript

js部分就很关键了,涉及到很多api,交互流程,编程思路,下面慢慢来讲:


一、 按功能设计代码模块

首先,按照功能分类,我们将代码分成3个模块

1. 第1部分paper.js 负责绘制出米字格的字帖背景 代码设计属性如下 paper.js

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var paper = {
        canvas: null,//html中的canvas对象,主要标签
        context: null, //canvas对象获取的context,用于绘图
        init: function (canvas) {
        //初始化方法
        },
        drawPaper: function(){
        //绘制米字格图纸的接口
        },

        drawDotted: function(sx, sy, ex, ey){
        //绘制虚线的接口
        }
    };

2. 第2部分controller.js 负责控制画笔的颜色和清理画布,即控制面板的功能实现。 代码设计属性如下 controller.js

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var controller = {
        canvas: null,//html中的canvas对象,主要标签
        context: null, //canvas对象获取的context,用于绘图
        init: function (canvas) {
        //初始化方法
        },
        bindEvent: function () {
        //事件监听的设置区域
        },

        setColor: function (target) {
        //设置画笔颜色的接口
        },

        clear: function () {
        //清理画布墨迹的接口
        }
    };

3. 第3部分write.js 第三部分也是最关键最复杂的部分,负责描绘出鼠标(手指)划过的笔画 因为要协调笔画,会用到比较多的辅助函数和辅助参数,下面会一一介绍 write.js

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var write = {
        canvas: null, //html中的canvas对象,主要标签
        context: null, //canvas对象获取的context,用于绘图
        isWriting: false,//是否正在下笔写字
        lineWidthMax: 0, //画笔最大粗细
        lineWidthMin: 1, //画笔最小粗细

        lastX: 0,//画笔上次停留位置
        lastY: 0,
        lastTime: 0, //上次移笔时间
        lastLineWidth: 0,//上次画下的笔宽
        init: function (canvas) {
        //初始化方法
        },
        bindEvent: function () {
        //事件监听的设置区域
        },

        //描绘区
        startWrite: function (co) {
        //开始落笔
        },
        writing: function (co) {
        //笔移动,正在写字
        },
        endWrite: function(){
        //收笔
        },

        //辅助函数区
        getCo: function (clientX, clientY) {
        //返回落笔的位置
        },
        getS: function (sx, sy, ex, ey) {
        //返回两点间的距离
        },
        getLineWidth: function (s, t) {
        //计算笔迹的线条宽度
        }
    };
二、 根据功能需要完善代码

1. 第1部分paper.js 针对paper模块,我们知道,是用来设置字帖纸的样式的。 我们要做的有两步

  • 一、 根据屏幕宽度设置米字格大小,兼容pc和手机
  • 二、 用虚线绘制出米字 (边框线条已经用css实现了)

初始化方法init()是一个模块最开始的地方。我们从 init开始,边看代码边解释 外接通过编写如下代码调用paper,完成其初始化,并让运行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var canvas = document.getElementById('cnavas');
paper.init(canvas);

所以我们在paper.init中接受外界传来的canvas,并利用它完成初始化,运行绘制方法,编写如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var paper = {
        canvas: null,//html中的canvas对象,主要标签
        context: null, //canvas对象获取的context,用于绘图
        init: function (canvas) {
            this.canvas = canvas;//接收外界canvas,赋值给自己的属性``canvas``,在下面的其他方法中需要用到
            this.context = canvas.getContext('2d');//通过canvas获取context,赋值给自己的属性``context``,在下面的其他方法中需要用到

            //动态设置canvas大小,兼容手机和pc
            this.canvas.width = Math.min(500, window.innerWidth - 20);// 米字格最大只能为500px
            this.canvas.height = this.canvas.width;

            this.drawPaper(); //绘制米字格背景,自己完善drawPaper方法
        },
        drawPaper: function() {},
        drawDotted: function(sx, sy, ex, ey){}
        //......
    };

drawPaper()方法就更加简单了,只需要调用drawDotted()接口绘制线段就可以了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//...
        drawPaper: function(){
            this.drawDotted(0, 0, this.canvas.width, this.canvas.height);//左上角到右下角的斜线
            this.drawDotted(this.canvas.width, 0, 0, this.canvas.height);//左下角到右上角的斜线
            this.drawDotted(this.canvas.width/2, 0, this.canvas.width/2, this.canvas.height);//中间的横线
            this.drawDotted(0, this.canvas.height/2, this.canvas.width, this.canvas.height/2);//中间的竖线
        },
        drawDotted: function(sx, sy, ex, ey){}
//...

drawDotted() 实现起来需要自己构思了,最新的h5标准已经有setLineDash用来绘制虚线,ie11以上都行,支持性不错,但是为了兼容,我们也尝试手写一些虚线函数出来

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//···
        drawDotted: function(sx, sy, ex, ey){
            var lineInterval = 5;//虚线的间隔
            this.context.save();//保存当前的context状态(快照,用于恢复,防止状态设置紊乱污染)

            this.context.lineWidth = 3;//线宽
            this.context.strokeStyle = '#ff1722';//线条颜色

            //setLineDash 虚线设置接口比较新,为了保险起见,自己编写一下
            if(this.context.setLineDash){ // 使用h5的setLineDash方法
                this.context.setLineDash([lineInterval, lineInterval]);//[线宽, 间隔宽]
                this.context.moveTo(sx,sy);
                this.context.lineTo(ex,ey);
            }
            else{//setLineDash不存在,自己手动处理
                //len 虚线要绘制成多少段
                var len = Math.ceil(Math.sqrt((ex - sx)*(ex - sx) + (ey - sy)*(ey - sy)) / lineInterval /2);
                var lineIntervalX = (ex - sx) / len;//每一段间x轴上的间隔
                var lineIntervalY = (ey - sy) / len;//每一段间y轴上的间隔
                var index = 0;//计数器
                this.context.beginPath();
                while (index < len) {//开始定制路线
                    var targetX = sx + lineIntervalX;//计算当前段绘制的终点
                    var targetY = sy + lineIntervalY;
                    this.context.moveTo(sx, sy);//起点
                    this.context.lineTo(targetX, targetY);//终点

                    sx = targetX + lineIntervalX;//计算下一段绘制的起点
                    sy = targetY + lineIntervalY;

                    index ++;
                }
            }

            this.context.stroke();//绘制线条
            this.context.restore();//恢复保存的状态,对应 save() 方法
        }
//···

如此,一个米字格图纸的绘制方法已经完成了,下面的代码汇总

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var paper = {
        canvas: null,//html中的canvas对象,主要标签
        context: null, //canvas对象获取的context,用于绘图
        init: function (canvas) {
            this.canvas = canvas;//接收外界canvas,赋值给自己的属性``canvas``,在下面的其他方法中需要用到
            this.context = canvas.getContext('2d');//通过canvas获取context,赋值给自己的属性``context``,在下面的其他方法中需要用到

            //动态设置canvas大小,兼容手机和pc
            this.canvas.width = Math.min(500, window.innerWidth - 20);// 米字格最大只能为500px
            this.canvas.height = this.canvas.width;

            this.drawPaper(); //绘制米字格背景,自己完善drawPaper方法
        },
        drawPaper: function(){
            this.drawDotted(0, 0, this.canvas.width, this.canvas.height);//左上角到右下角的斜线
            this.drawDotted(this.canvas.width, 0, 0, this.canvas.height);//左下角到右上角的斜线
            this.drawDotted(this.canvas.width/2, 0, this.canvas.width/2, this.canvas.height);//中间的横线
            this.drawDotted(0, this.canvas.height/2, this.canvas.width, this.canvas.height/2);//中间的竖线
        },

        drawDotted: function(sx, sy, ex, ey){
            var lineInterval = 5;//虚线的间隔
            this.context.save();//保存当前的context状态(快照,用于恢复,防止状态设置紊乱污染)

            this.context.lineWidth = 3;//线宽
            this.context.strokeStyle = '#ff1722';//线条颜色

            //setLineDash 虚线设置接口比较新,为了保险起见,自己编写一下
            if(this.context.setLineDash){ // 使用h5的setLineDash方法
                this.context.setLineDash([lineInterval, lineInterval]);//[线宽, 间隔宽]
                this.context.moveTo(sx,sy);
                this.context.lineTo(ex,ey);
            }
            else{//setLineDash不存在,自己手动处理
                //len 虚线要绘制成多少段
                var len = Math.ceil(Math.sqrt((ex - sx)*(ex - sx) + (ey - sy)*(ey - sy)) / lineInterval /2);
                var lineIntervalX = (ex - sx) / len;//每一段间x轴上的间隔
                var lineIntervalY = (ey - sy) / len;//每一段间y轴上的间隔
                var index = 0;//计数器
                this.context.beginPath();
                while (index < len) {//开始定制路线
                    var targetX = sx + lineIntervalX;//计算当前段绘制的终点
                    var targetY = sy + lineIntervalY;
                    this.context.moveTo(sx, sy);//起点
                    this.context.lineTo(targetX, targetY);//终点

                    sx = targetX + lineIntervalX;//计算下一段绘制的起点
                    sy = targetY + lineIntervalY;

                    index ++;
                }
            }

            this.context.stroke();//绘制线条
            this.context.restore();//恢复保存的状态,对应 save() 方法
        }
    };

如此,在html中调用 paper.init(canvas), 一个米字格就会成功绘制在你的面前,是不是很简单又有趣

边幅有点长,这是绘制的第一部分,我们在接下来的一篇博客里再讲第二部分,请期待 利用canvas实现毛笔字帖(二), 跟大家真正实现毛笔写字的部分

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
利用canvas实现毛笔字帖(二)
2. 第2部分write.js 第二部分决定先介绍write部分,因为controller部分必须要结合write部分才能看到效果。 针对write.js部分,前面有介绍,是用来实现通过鼠标(手指)写字的核心部分。 分析一下要做的事情。
用户1394570
2018/08/08
3.6K0
利用canvas实现毛笔字帖(二)
利用canvas实现毛笔字帖(三)
3. 第3部分controller.js 这一部分的功能就是要修改毛笔的颜色,还有清空画布。 功能简单,我们一起向下讲。 一开始依然是init controller.js
用户1394570
2018/08/02
1.9K0
利用canvas实现毛笔字帖(三)
Canvas基础-粒子动画Part3
在上一篇文章 Canvas基础-粒子动画Part2 中讲了让图片做粒子动画。上篇写完不久,想起既然是用Canvas,写上去的东西都可以做粒子动画,那么就补充讲下文字做粒子动画,至于为什么拖了这么久,还不是因为去写公众号和研究微信小程序了,给公众号搞了一个2048形式的小游戏,叫『码工修炼之路』,在公众号 『程序员的诗和远方』回复关键字 2048 就可以玩了。 文字做粒子动画 上一篇中我们是把图片给画到 Canvas 中,要写文字进去就简单多,直接有方便的接口就可以做,我们来试试, 先在页面上添加一个输
Bob.Chen
2018/05/02
1K0
Canvas基础-粒子动画Part3
html5点击出现燃放烟花特效
今天我发现了一个非常好的html特效,是由HTML5来实现的,效果非常绚丽。效果如下:
OECOM
2020/07/01
3.4K0
Canvas
http://www.w3c.org/TR/2dcontext/ https://html.spec.whatwg.org/
jinghong
2020/05/09
13.2K0
Canvas
绚丽烟花:HTML5 Canvas 烟花效果实现(文末附完整代码)
在节日或特殊场合,绚丽的烟花总能带来无尽的欢乐和惊喜。今天,我们将通过 HTML5 Canvas 实现一个绚丽的烟花效果,让你的网页也能绽放出美丽的烟花。
码事漫谈
2025/01/27
9931
绚丽烟花:HTML5 Canvas 烟花效果实现(文末附完整代码)
第155天:canvas(二)
​ 这个属性影响到 canvas 里所有图形的透明度,有效的值范围是 0.0 (完全透明)到 1.0(完全不透明),默认是 1.0。
半指温柔乐
2018/09/11
5080
第155天:canvas(二)
Canvas 设置样式 lineWidth、lineCap、lineJoin、strokeStyle、fillStyle、setLineDash、getLineDash、lineDashOffset
上面绘画了三条平行线设置了末端样式,下面给三条平行线进行打折,使用lineJoin的方式设置拐点的样式。
Devops海洋的渔夫
2020/02/13
1.9K0
Canvas实现刮奖效果
https://github.com/muzqi/Article/blob/master/blog/canvas_1_eraser.md
治电小白菜
2020/08/25
7520
Canvas实现刮奖效果
超级玛丽HTML5源代码学习------(二)
首先放置源代码: <!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="-1" /> <meta http-equiv="Cache-Control" content="no-cache" />
wust小吴
2022/03/03
1.8K0
超级玛丽HTML5源代码学习------(二)
Canvas高级
1.基本语法 <script> var can = document.getElementById("can"), context = can.getContext("2d"); can.width = 600; can.height =800; can.style.border = "1px solid red"; /*cancas是基于状态的绘制的,而不是对某一个线条或者框框设置,是对整个环境的定义*/ /*意思就是说设
天天_哥
2018/09/29
9490
利用canvas给图片加水印 (转)
如果仅仅是普通的合成,例如一个底图和一个PNG水印图片合成,直接使用canvas的drawImage()方法即可,语法如下:
javascript.shop
2019/09/04
4.8K0
利用canvas给图片加水印 (转)
Canvas
注意canvas 的 width 和 height 不要用 css 来设定,如果用 css 样式来设置,会变形和失真
小丞同学
2021/08/16
1.3K0
超级玛丽HTML5源代码学习-----(三)
定义和用法 setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。 setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。 语法 setInterval(code,millisec[,"lang"]) 参数 描述 code 必需。要调用的函数或要执行的代码串。 millisec 必须。周期性执行或调用 code 之间的时间间隔,以毫秒计。 返回值 一个可以传递给 Window.clearInterval() 从而取消对 code 的周期性执行的值。
wust小吴
2022/03/03
1.3K0
Canvas 从入门到劝朋友放弃(图解版)
在前端领域,如果只是懂 Vue 或者 React ,未来在职场的竞争力可能会比较弱。
德育处主任
2022/09/02
2K0
Canvas 从入门到劝朋友放弃(图解版)
canvas 像素操作
在 canvas 中可以使用 context.drawImage(image,dx,dy) 方法将图片绘制在 canvas 上。将图片绘制上去后,还可以使用 context.getImageData(sx, sy, sw, sh) 方法获取 canvas 区域隐含的像素数据,该方法返回一个 ImageData 对象,里面包含的是 canvas 像素信息。
多云转晴
2020/01/14
1.9K0
canvas 像素操作
波浪动态背景
itclanCoder
2023/09/14
3270
波浪动态背景
vue-piczoom基于vue2.x的电商图片放大镜插件二次开发记录
vue-piczoom 基于vue2.x的电商图片放大镜插件,修复了图片放大的一些bug,看懂的就看看,看不懂的就算了,太忙没时间写笔记备注了,可以自己用对比工具去看下修改了哪些
李维亮
2021/07/08
1.1K0
鸿蒙元服务实战-笑笑五子棋(2)
Canvas提供画布组件,用于自定义绘制图形,开发者使用 CanvasRenderingContext2D 对象和 OffscreenCanvasRenderingContext2D
万少
2025/02/08
1880
鸿蒙元服务实战-笑笑五子棋(2)
超级玛丽HTML5源代码学习------(一)
<pre name="code" class="html"><!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="-1" /> <meta http-equiv="Cache-Control" co
wust小吴
2022/03/03
1.4K0
推荐阅读
相关推荐
利用canvas实现毛笔字帖(二)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验