原 canvas小案例集合(小画板、画的回

作者:汪娇娇

日期:2016.12.8

在现在这个公司呆了4个多月,也是研究了canvas将近4个月,前两周心里就痒痒的想写这方面的博客,但一直没时间。可一直拖着也不是个办法,就这样抽抽空来写吧。

canvas的常用方法我就不介绍了,主要是给大家展示几个常用的canvas案例,也是自己做过的一些小东西,希望能对大家有帮助吧。

案例一:小画板

这个应该是canvas最常用的案例之一了:小画板,也就是鼠标画画。PC端和手机端皆有兼容:

成果图:

代码如下:

小提醒:为了兼容手机端,记得加上viewport:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

1、html代码:

<body>
    <!--画布-->
    <div class="box">
        <canvas id="canvas"></canvas>
    </div>
    
    <!--颜色-->
    <div class="color">
        <span></span>
        <span class="active"></span>
        <span></span>
        <span></span>
        <span></span>
        <span></span>
    </div>
    
    <!--橡皮擦和清除画布-->
    <div class="clear">
        <button id="eraser">橡皮擦</button>
        <button id="btn">清除画布</button>
    </div>
</body>

2、css代码:

<style>
    body{
        margin:0;
        padding:0;
        background: #f1f1f1;
    }
    .box{
        width: 100vw;
        overflow: hidden;
        display: flex;
        justify-content: center;
    }
    #canvas{
        background: #EBE9BB;
    }
    .color{
        overflow: hidden;
        display: flex;
        justify-content: center;
        align-items: center;
        padding: 10px 0;
    }
    .color span{
        display: block;
        width: 40px;
        height: 40px;
        border-radius: 50%;
        margin-right: 10px;
    }
    .active{
        border: 3px solid darkmagenta;
    }
    .clear{
        overflow: hidden;
        text-align: center;
    }
    .clear button{
        padding: 7px 15px;
        background: #008000;
        border-radius: 5px;
        color: #fff;
        font-size: 5vw;
        border: 0;
    }
    #btn{
        background: #ff328e!important;
    }
</style>

3、js代码:

注:我是提前引入jQuery的:

<script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<script>
    var w=document.body.clientWidth;
    var width,x1,y1,x2,y2,a=10;
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    
    //画布宽高初始化
    if(w>=320 && w<=480){
        canvas.width=w;
        canvas.height=w;
        width=w;
    }else if(w>480){
        canvas.width=480;
        canvas.height=480;
        width=w;
    }
    
    //颜色初始化
    var colors="green";
    var color=["black","green","red","gray","blue","yellow"];
    for(var i=0;i<color.length;i++){
        var j=i+1;
        $(".color>span:nth-child("+j+")").css("background",color[i]);
    }
    
    //选颜色
    $(".color>span").click(function(){
        $(this).addClass("active");
        $(this).siblings().removeClass("active");
        colors=$(this)[0].style.background; 
        a=10;
        tapClip();
    })
    
    //点击“橡皮擦”按钮
    $("#eraser").click(function(){
        colors="#EBE9BB";
        a=30;
        tapClip();
    })
    
    //点击“清除画板”按钮
    $("#btn").click(function(){
        ctx.clearRect(0,0,width,width);
    })
    
    tapClip();
    //在画板上画画的函数
    function tapClip(){
        //区分PC还是手机
        var hastouch = "ontouchstart" in window ? true:false,
            tapstart = hastouch ? "touchstart":"mousedown",
            tapmove = hastouch ? "touchmove":"mousemove",
            tapend = hastouch ? "touchend":"mouseup";
        
        //笔触圆滑
        ctx.lineCap = "round";
        ctx.lineJoin = "round";
        ctx.lineWidth = a;
        ctx.strokeStyle=colors;
        
        //touchstart或者mousedown
        canvas.addEventListener(tapstart , function(e){
            e.preventDefault();
            x1 = hastouch?e.targetTouches[0].pageX-canvas.offsetLeft:e.clientX-canvas.offsetLeft;
            y1 = hastouch?e.targetTouches[0].pageY-canvas.offsetTop:e.clientY-canvas.offsetTop;
            ctx.beginPath()
            ctx.moveTo(x1,y1);
            ctx.lineTo(x1+1,y1+1);  //考虑到点击时能画出一个点
            ctx.stroke();
            
            //touchmove或者mousemove
            canvas.addEventListener(tapmove , tapmoveHandler);
            
            //touchend或者mouseup
            canvas.addEventListener(tapend , function(){
                canvas.removeEventListener(tapmove , tapmoveHandler);
            });
            
            //move时的处理函数
            function tapmoveHandler(e){
                e.preventDefault()
                x2 = hastouch?e.targetTouches[0].pageX-canvas.offsetLeft:e.clientX-canvas.offsetLeft;
                y2 = hastouch?e.targetTouches[0].pageY-canvas.offsetTop:e.clientY-canvas.offsetTop;                
                ctx.moveTo(x1,y1);
                ctx.lineTo(x2,y2);
                ctx.stroke();
                x1 = x2;
                y1 = y2;
            }
        })
    }
</script>

案例二:画的回放

方法:结合getImageData()和putImageData()

原理:通过 getImageData() 复制画布上的像素数据,然后通过 putImageData() 将图像数据放回画布。

getImageData()格式:

ImageData下面的data就是构成画布上的像素数据。

效果图如下:

以下代码仅以PC端为例:

1、html代码:

<body>
    <div class="box">
        <canvas id="canvas" width=320px" height="320px"></canvas>
    </div>
    
    <button id="btn">点击回放</button>
</body>

2、css代码:

<style>
    *{
        margin: 0;
        padding: 0;
    }
    .box{
        width: 100vw;
        overflow: hidden;
        display: flex;
        justify-content: center;
        margin-bottom: 10px;
    }
    #canvas{
        background: #EBE9BB;
    }
    #btn{
        display: block;
        margin: 0 auto;
        padding: 7px 15px;
        background: #ff328e;
        border-radius: 5px;
        color: #fff;
        font-size: 20px;
        border: 0;
    }
</style>

3、js代码:

<script>
	var w=320;
    var width,arr=[];
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    var btn=document.getElementById("btn");
    
    //鼠标按下
    canvas.onmousedown=function(event){
        ctx.moveTo(event.pageX-canvas.offsetLeft,event.pageY-canvas.offsetTop)
        ctx.lineTo(event.pageX-canvas.offsetLeft+1,event.pageY-canvas.offsetTop+1)
        ctx.stroke();
        //获取像素,存到数组
        var imageData=ctx.getImageData(0,0,w,w);
        arr.push(imageData);
        //鼠标移动
        canvas.onmousemove=function(event){
            ctx.lineTo(event.pageX-canvas.offsetLeft,event.pageY-canvas.offsetTop)
            ctx.stroke();
            //获取像素,存到数组
            var imageData=ctx.getImageData(0,0,w,w);
            arr.push(imageData);
        }
        //鼠标释放
        canvas.onmouseup=function(){
            canvas.onmousemove=null;
            canvas.onmousestart=null;
        }
    }	   
    
    //点击“点击回放”按钮
    btn.onclick=function(){
        ctx.clearRect(0,0,w,w);
        var i=0;
        var timer;
        clearInterval(timer);
        //利用定时器回放像素
        timer=setInterval(function(){
            if(arr.length==0){
                clearInterval(timer);
            }else{
                ctx.putImageData(arr[i],0,0);
                i++;
                if(i>=arr.length){
                    clearInterval(timer);
                }
            }
        },10)
    }
</script>

这是其中一个办法,还有一个办法就是通过存储坐标点,然后用定时器回放坐标点。方法类似,大家可以自己研究研究。

案例三:刮刮乐()

原理:ctx.globalCompositeOperation = "destination-out";

在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。

效果图:

1、html代码:

<body>
	<div class="box" id="bb">
		<canvas id="canvas"></canvas>
	</div>
</body>

2、css代码:

<style>
	body{
		margin:0;
		padding:0;
	}
	.box{
	    width: 100vw;
	    height: 30vh;
		position: absolute;
		left:0;
		top:0;
		overflow: hidden;
	}
</style>

3、js代码:

<script>
	var canvas = document.getElementById("canvas");
	var ctx = canvas.getContext("2d");
	var x1,y1,a=60;
	canvas.width = document.getElementById("bb").clientWidth;
	canvas.height = document.getElementById("bb").clientHeight;
	var w=$("#canvas").width();
    var h=$("#canvas").height();
    
    //加载图片
	var img = new Image();
	img.src = "dim.png";
	img.onload = function(){
		ctx.drawImage(img,0,0,canvas.width,canvas.height);
		$(".box").css("background","url('noScratch.png') no-repeat");
		$(".box").css("background-size","100% 100%");
		tapClip();
	}
	
    //在小画板上画画的函数
	function tapClip(){
		var hastouch = "ontouchstart" in window ? true:false,
			tapstart = hastouch ? "touchstart":"mousedown",
			tapmove = hastouch ? "touchmove":"mousemove",
			tapend = hastouch ? "touchend":"mouseup";

		ctx.lineCap = "round";
		ctx.lineJoin = "round";
		ctx.lineWidth = a;
		
		//在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。
		ctx.globalCompositeOperation = "destination-out";
		
        //touchstart或mousedown
		canvas.addEventListener(tapstart , function(e){
			e.preventDefault();
			ctx.beginPath();
            //touchmove或mousemove
			canvas.addEventListener(tapmove , tapmoveHandler);
			canvas.addEventListener(tapend , function(){
				canvas.removeEventListener(tapmove , tapmoveHandler);
                //获取像素
				var data = ctx.getImageData(0,0,w,h).data;
				for (var i=0,j=0;i<data.length;i+=4){
                    if(data[i]&&data[i+1]&&data[i+2]&&data[i+3]){
                        j++;
                    }
                }
				//刮到60%时显示全部
				if(j<=canvas.width*canvas.height*0.6){
                    ctx.clearRect(0,0,w,h);
                }
			});
            //move时的处理函数
			function tapmoveHandler(e){
				e.preventDefault()
				x2 = hastouch?e.targetTouches[0].pageX:e.clientX-canvas.offsetLeft;
				y2 = hastouch?e.targetTouches[0].pageY:e.clientY-canvas.offsetTop;
				
				ctx.moveTo(x1,y1);
				ctx.lineTo(x2,y2);
				ctx.stroke();
				
				x1 = x2;
				y1 = y2;
			}
		})
	}
</script>

思考:刮刮乐的方法可以延伸到彩色画笔,大家自己思考一下哈,如下图:

canvas暂时就到这儿了,有问题可以在下面回复或私聊哈。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏点滴积累

geotrellis使用(五)使用scala操作Accumulo

    要想搞明白Geotrellis的数据处理情况,首先要弄清楚数据的存放,Geotrellis将数据存放在Accumulo中。     Accumulo是一...

44540
来自专栏点滴积累

geotrellis使用初探

最近,单位领导要求我研究一下geotrellis(GITHUB地址:https://github.com/geotrellis/geotrellis,官网htt...

47780
来自专栏顶级程序员

6 个新奇的编程方式,改变你对编码的认知

源 | Reddit 译 | OSC - 周其 我时不时会发现一种编程语言的不同用法它有时候会改变我对编程的看法啊。这篇文章中,我想分享一下让我惊讶的发...

30040
来自专栏点滴积累

编程的思想性——议编程与“武功”的一致性

一、缘起        最近做了一件事情,将写好的scala程序中稍显混乱和不雅的代码进行了重构(系列博客见http://www.cnblogs.com/sho...

37250
来自专栏点滴积累

使用bokeh-scala进行数据可视化

目录 前言 bokeh简介及胡扯 bokeh-scala基本代码 我的封装 总结 一、前言        最近在使用spark集群以及geotrellis框架(...

66580
来自专栏点滴积累

geotrellis使用(六)Scala并发(并行)编程

      本文主要讲解Scala的并发(并行)编程,那么为什么题目概称geotrellis使用(六)呢,主要因为本系列讲解如何使用Geotrellis,具体前...

38750
来自专栏点滴积累

一个程序员的思考

读书笔记系列链接地址http://www.cnblogs.com/shoufengwei/p/5714661.html。        最近被一件事情虐的不行...

36850
来自专栏AI科技大本营的专栏

Python 为何能坐稳 AI 时代头牌语言

作者 | 子白 谁会成为AI 和大数据时代的第一开发语言?这本已是一个不需要争论的问题。如果说三年前,Matlab、Scala、R、Java 和 Python还...

51060
来自专栏Windows Community

UWP 手绘视频创作工具技术分享系列 - SVG 的解析和绘制

本篇作为技术分享系列的第一篇,详细讲一下 SVG 的解析和绘制,这部分功能的研究和最终实现由团队的 @黄超超 同学负责,感谢提供技术文档和支持。  首先我们来看...

42490
来自专栏大数据-Hadoop、Spark

Scala学习一

1.集合操作练习 //创建一个List val lst0 = List(1,7,9,8,0,3,5,4,6,2) //将lst0中每个元素乘以10后生成一个新的...

63060

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励