zoukankan      html  css  js  c++  java
  • canvas-画图改进版

    前几天在canvas——画板中做了个很简陋的画板,只能画简单的线条,可以选择颜色和线条粗度,今天在此简陋的画板上增加了新的形状,撤销,保存,橡皮擦等功能,虽然功能还是很简单,刚接触canvas,过程中还是遇到了很多困难。

    形状包括:铅笔、直线、直角矩形,圆角矩形、原型(利用圆角矩形能够画出很多其他好玩的图形)

    提供橡皮擦、撤销、重做、清屏、保存功能

    1、html中使用两个canvas,一个相当于蒙版用于缓存

    <div id="imgContent">
            <canvas id="canvas" width="600" height="490">
                浏览器不支持~
            </canvas>
            <!--此canvas用于暂存graph,绘制过程中需要清除context,因而需要缓存,否则更换形状时会出现问题 -->
            <canvas id="canvasTemp"width="600" height="490" >&nbsp;</canvas>
        </div>

    2、主要鼠标事件:

            $(canvasTemp).unbind();
            $(canvasTemp).bind('mousedown',down);
            $(canvasTemp).bind('mousemove',move);
            $(canvasTemp).bind('mouseup',up);
            $(canvasTemp).bind('mouseout',out);

    3、更换图形时需清空蒙版canvas即canvasTemp的context环境,并且将上次绘制内容添加到canvas中

    4、另外需要为各种形状添加鼠标移动时的图标,例如选择circle时鼠标未按下时需要绘制一个小圆

        else if(type === "circle"){
                    clearContext();
                    if(mouseState === true){ //鼠标按下时
                        ctxTemp.beginPath();
                        var radius = Math.sqrt((oldX - newX) *  (oldX - newX)  + (oldY - newY) * (oldY - newY));
                        ctxTemp.arc(oldX,oldY,radius,0,Math.PI * 2,false);                                    
                        ctxTemp.stroke();
                    }else{
                        //鼠标没有按下时出现小圆
                        ctxTemp.beginPath();
                        //ctxTemp.strokeStyle ='#9F35FF';    
                        ctxTemp.arc(newX,newY,10 ,0, Math.PI * 2,false);
                        ctxTemp.stroke();
                    }

    5、需要存储绘制的过程,以便撤销和重做

        function saveImageHistory(){
            cancelTimes = 0;
            imageHistoryList.push(canvas.toDataURL());
            if(imageHistoryList.length > 0){
                document.getElementById("undoImage").src="./images/undo.png";
            }
        }

    6、由于body背景设置为蓝色,canvas fill 的是白色,因而用橡皮擦时如果直接用clearRect会擦出蓝色背景,因而采用填充白色代替

    //ctx.clearRect(newX - lineWeight * 10 ,  newY - lineWeight * 10 , lineWeight * 20 , lineWeight * 20);
     //重新填充白色背景,否则擦出后是颜色背景
     ctx.fillStyle = "white";
    ctx.fillRect(newX
    - lineWeight * 10 , newY - lineWeight * 10 , lineWeight * 20 , lineWeight * 20);

    7.问题总结:

     1)body为蓝色背景,因而想将canvas背景设置为白色,然后尝试将canvas设置背景,这种方式是不行的,只能使用fillRect方式为canvas填充背景,此种方式为橡皮擦的擦除带来了问题,橡皮擦的擦除应该也使用fillRect方式而不能使用clearRect方式

       2)DOM中定义的id会再javasvript中以变量自动定义,因而一定注意js中与id同名的变量或者方法可能不起作用

     3)使用原生js时事件解绑中遇到问题,尝试了removeEventListener和最原始的将绑定事件置为null,但是没起作用,没找到解决方法因而使用了jQuery的unbind,希望有人指导

    8.代码:

    var canvas,
            ctx,
            canvasTemp,
            ctxTemp,
            mouseState = false, //初始化鼠标是否按下和坐标点位置, true为按下
            oldX = 0,
            oldY = 0,
            pencilX = 0,
            pencilY = 0,
            lineColor = "black",
            lineWeight = 1,
            canvasTop,
            canvasLeft,
            canvasWidth = 700,
            canvasHeight = 550,
            cancelTimes = 0, //撤销次数
            imageHistoryList = new Array(); //存储图片绘制历史信息
            
        
        onLoad(function(){
            init(); //初始化canvas
            
            //颜色和线宽绑定点击事件
            var colorDiv = document.getElementById("color");
            var lineDiv = document.getElementById("lineWeight");
            colorDiv.addEventListener("click", chosen);
            lineDiv.addEventListener("click", chosen);
            
            document.getElementById("pencil").click(); //未选择图形时默认为铅笔
            document.getElementById("blackBtn").click(); //默认黑色
            document.getElementById("line1").click(); //默认线宽2px
        });
        
        var chosen = function(event){
            var parentNode = event.target.parentNode;
            for(var i=0; i<parentNode.childNodes.length; i++){
                parentNode.childNodes[i].className = "";
            }
            event.target.className = "chosen";
        };
        
        var init = function(){
            //初始化canvas
            canvas = document.getElementById("canvas");
            canvas.width = canvasWidth;
            canvas.height = canvasHeight;
            //判断是否支持canvas
            if(!canvas || !canvas.getContext){
                return false;
            }
            ctx = canvas.getContext("2d");
            
            //初始化画图区域白色背景
            ctx.fillStyle = "white";
            ctx.fillRect(0, 0, 700, 550);
            
            //初始化canvasTemp
            canvasTemp = document.getElementById("canvasTemp");
            canvasTemp.width = canvasWidth;
            canvasTemp.height = canvasHeight;
            ctxTemp = canvasTemp.getContext("2d");
            
            canvasTop = canvas.offsetTop,
            canvasLeft = canvas.offsetLeft;
            
            //初始化撤销和重做按钮状态
            document.getElementById("undoImage").src="./images/undoDis.png";
            document.getElementById("redoImage").src="./images/redoDis.png";
    
        };
    
        //绘制picture
        var drawPicture = function(type, obj){
            var down, //鼠标按下事件
                up, //鼠标弹起事件
                move, //鼠标移动事件
                out, //鼠标离开区域
                chosen, //图形选中
                clearContext; //清除canvas环境
            
            down = function(event){
                mouseState = true;
                event = event || window.event;
                
                oldX =  event.clientX - canvasLeft;
                pencilX =  event.clientX - canvasLeft;
                oldY = event.clientY - canvasTop;
                pencilY = event.clientY - canvasTop;
                
                ctxTemp.strokeStyle = lineColor;
                ctxTemp.lineWidth = lineWeight;
                ctxTemp.lineCap = "round";
                
                clearContext();
                
                ctxTemp.moveTo(oldX, oldY);
                
                if(type === "rubber"){
                    //ctx.clearRect(oldX-lineWeight*10, oldY-lineWeight*10, lineWeight*20, lineWeight*20);
                    //重新填充白色背景,否则擦出后是颜色背景
                    ctx.fillStyle = "white";
                    ctx.fillRect(oldX-lineWeight*10, oldY-lineWeight*10, lineWeight*20, lineWeight*20);
                }
            };
        
            up = function(event){
                //更改鼠标状态
                mouseState = false;
                event = event || window.event;
                
                //将canvasTemp中graph添加到canvas中
                var image = new Image();
                if(type !== "rubber"){
                    image.src = canvasTemp.toDataURL();
                    image.onload = function(){
                        ctx.drawImage(image, 0, 0, image.width, image.height);
                        clearContext();
                        
                        //保存历史记录,撤销时使用
                        saveImageHistory();
                        
                    };
                }
                
            };
        
            chosen = function(obj){
                var shape = document.getElementById("shape");
                for(var i=0; i<shape.childNodes.length; i++){
                    shape.childNodes[i].className = "";
                }
                if(type !== "rubber"){
                    document.getElementById("rubber").className = "";
                }
                obj.className = "chosen";
            };
            
            //鼠标按下,拖动画图
            move = function(event){
                var newX = event.clientX - canvasLeft;
                var newY = event.clientY - canvasTop;
                if(type === "pencil"){
                    if(mouseState === true){
                        ctxTemp.beginPath();
                        ctxTemp.moveTo(pencilX, pencilY);
                        ctxTemp.lineTo(newX, newY);
                        ctxTemp.stroke();
                        pencilX = newX;
                        pencilY = newY;
                    }
                }else if(type === "rec"){
                    clearContext();
                    if(mouseState === true){
                        ctxTemp.beginPath();
                        ctxTemp.moveTo(oldX, oldY);
                        ctxTemp.lineTo(newX, oldY);
                        ctxTemp.lineTo(newX, newY);
                        ctxTemp.lineTo(oldX, newY);
                        ctxTemp.lineTo(oldX, oldY);
                        ctxTemp.stroke();
                    }else{
                        //鼠标移动时出现矩形
                        ctxTemp.beginPath();    
                        ctxTemp.moveTo(newX - 10 ,  newY - 10 );                        
                        ctxTemp.lineTo(newX + 10  , newY - 10 );
                        ctxTemp.lineTo(newX + 10  , newY + 10 );
                        ctxTemp.lineTo(newX - 10  , newY + 10 );
                        ctxTemp.lineTo(newX- 10  , newY - 10 );    
                        ctxTemp.stroke();
                    }
                }else if(type === "line"){
                    if(mouseState === true){
                        ctxTemp.beginPath();
                        clearContext();
                        ctxTemp.moveTo(oldX, oldY);
                        ctxTemp.lineTo(newX, newY);
                        ctxTemp.stroke();
                    }
                }else if(type === "circle"){
                    clearContext();
                    if(mouseState === true){
                        ctxTemp.beginPath();
                        var radius = Math.sqrt((oldX - newX) *  (oldX - newX)  + (oldY - newY) * (oldY - newY));
                        ctxTemp.arc(oldX,oldY,radius,0,Math.PI * 2,false);                                    
                        ctxTemp.stroke();
                    }else{
                        //鼠标没有按下时出现小圆
                        ctxTemp.beginPath();
                        //ctxTemp.strokeStyle ='#9F35FF';    
                        ctxTemp.arc(newX,newY,10 ,0, Math.PI * 2,false);
                        ctxTemp.stroke();
                    }
                }else if(type === "roundRec"){
                    clearContext();
                    if(mouseState === true){
                        ctxTemp.beginPath();
                        ctxTemp.moveTo(oldX, oldY);
                        ctxTemp.lineTo(newX, oldY);
                        ctxTemp.arcTo(newX+20,oldY, newX+20, oldY+20, 20);
                        ctxTemp.lineTo(newX+20, newY);
                        ctxTemp.arcTo(newX+20,newY+20, newX, newY+20, 20);
                        ctxTemp.lineTo(oldX, newY+20);
                        ctxTemp.arcTo(oldX-20,newY+20, oldX-20, newY, 20);
                        ctxTemp.lineTo(oldX-20, oldY+20);
                        ctxTemp.arcTo(oldX-20,oldY, oldX, oldY, 20);
                        ctxTemp.stroke();
                    }else{
                        //鼠标没有按下时出现小的圆角矩形
                        ctxTemp.beginPath();
                        //ctxTemp.strokeStyle ='#9F35FF';    
                        ctxTemp.moveTo(newX - 10 ,  newY - 10);
                        ctxTemp.lineTo(newX, newY - 10);
                        ctxTemp.arcTo(newX + 10,newY - 10, newX + 10, newY, 10);
                        ctxTemp.lineTo(newX + 10, newY + 10);
                        ctxTemp.arcTo(newX + 10, newY + 20, newX, newY + 20, 10);
                        ctxTemp.lineTo(newX - 10, newY + 20);
                        ctxTemp.arcTo(newX - 20,newY + 20, newX - 20,newY + 10,10);
                        ctxTemp.lineTo(newX - 20,newY);
                        ctxTemp.arcTo(newX - 20,newY - 10, newX - 10,newY - 10, 10);
                        ctxTemp.stroke();
                    }
                }else if(type === "rubber"){
                    //鼠标没有按下时出现橡皮擦图标
                    ctxTemp.beginPath();
                    clearContext();
                    ctxTemp.strokeStyle =  '#000000';                        
                    ctxTemp.moveTo(newX - lineWeight * 10 ,  newY - lineWeight * 10 );                        
                    ctxTemp.lineTo(newX + lineWeight * 10  , newY - lineWeight * 10 );
                    ctxTemp.lineTo(newX + lineWeight * 10  , newY + lineWeight * 10 );
                    ctxTemp.lineTo(newX - lineWeight * 10  , newY + lineWeight * 10 );
                    ctxTemp.lineTo(newX- lineWeight * 10  , newY - lineWeight * 10 );    
                    ctxTemp.stroke();    
                    if(mouseState === true){
                        //ctx.clearRect(newX - lineWeight * 10 ,  newY - lineWeight * 10 , lineWeight * 20 , lineWeight * 20);
                        //重新填充白色背景,否则擦出后是颜色背景
                        ctx.fillStyle = "white";
                        ctx.fillRect(newX - lineWeight * 10 ,  newY - lineWeight * 10 , lineWeight * 20 , lineWeight * 20);
    
                        
                    }
                }
                
            };
            
            out = function(){
                clearContext();
            };
            clearContext = function(){
                ctxTemp.clearRect(0,0,canvas.width,canvas.height);
            };
            
            //将选中的形状置为选中状态
            chosen(obj);
            
            //canvas添加鼠标事件, 鼠标移动、鼠标按下和鼠标弹起
            /*
            canvasTemp.addEventListener("mousemove", move);
            canvasTemp.addEventListener("mousedown", down);
            canvasTemp.addEventListener("mouseup", up);
            canvasTemp.addEventListener("mouseout", out);
            */
            
            /*
             * 本来尝试使用原生js来写,但是在上面的事件解绑中遇到问题
             * 尝试了removeEventListener和最原始的将绑定事件置为null,但是没起作用,没找到解决方法因而使用了jQuery的unbind
             * 
             */
            
            $(canvasTemp).unbind();
            $(canvasTemp).bind('mousedown',down);
            $(canvasTemp).bind('mousemove',move);
            $(canvasTemp).bind('mouseup',up);
            $(canvasTemp).bind('mouseout',out);
        };
        
        
        /*
         * 保存picture历史记录
         */
        function saveImageHistory(){
            cancelTimes = 0;
            imageHistoryList.push(canvas.toDataURL());
            if(imageHistoryList.length > 0){
                document.getElementById("undoImage").src="./images/undo.png";
            }
        }
        
        var  exportImage = function(event){
                var imgSrc = canvas.toDataURL("image/png");
                document.getElementById("image").src = imgSrc;
            };
        
        /*
         * undo 撤销一次
         */
        var undo = function(){
            cancelTimes++;
            if(cancelTimes >= imageHistoryList.length+1){
                cancelTimes--;
                return;
            }else if(cancelTimes == imageHistoryList.length){
                document.getElementById("redoImage").src="./images/redo.png";
                ctx.clearRect(0, 0, canvasWidth, canvasHeight);
                document.getElementById("undoImage").src="./images/undoDis.png";
            }else{
                document.getElementById("redoImage").src="./images/redo.png";
                ctx.clearRect(0, 0, canvasWidth, canvasHeight);
                var image = new Image();
                image.src = imageHistoryList[imageHistoryList.length-1-cancelTimes];
                image.onload = function(){
                    ctx.drawImage(image, 0, 0, image.width, image.height);
                };
            }
        };
        
        /*
         * redo,重做上一次操作
         */
        var redo = function(){
            cancelTimes--;
            if(cancelTimes < 0){
             cancelTimes++;
                return;
            }else{
                if(cancelTimes == 0){
                    document.getElementById("redoImage").src="./images/redoDis.png";
                    document.getElementById("undoImage").src="./images/undo.png";
                }
                ctx.clearRect(0, 0, canvasWidth, canvasHeight);
                var image = new Image();
                image.src = imageHistoryList[imageHistoryList.length-1-cancelTimes];
                image.onload = function(){
                    ctx.drawImage(image, 0, 0, image.width, image.height);
                };
            }
        };
        
        /**
         *清屏 
         */
        function clearScreen(){
            ctx.clearRect(0, 0, canvasWidth, canvasHeight);
            ctxTemp.clearRect(0, 0, canvasWidth, canvasHeight);
        }
        
    
        /**
        * 工具函数onLoad,当文档载入完成时调用一个函数
        */
        function onLoad(f){
            if(onLoad.loaded){
                window.setTimeout(f,0);
            }else if(window.addEventListener){
                window.addEventListener("load",f,false);
            }else if(window.attachEvent){
                window.attachEvent("onload",f);
            }
        }
        onLoad.loaded = false;
        onLoad(function(){
            onLoad.loaded = true;
        });
        
  • 相关阅读:
    27. Remove Element
    列表变成字典
    1. Two Sum
    CVPR2019:What and How Well You Performed? A Multitask Learning Approach to Action Quality Assessment
    959. Regions Cut By Slashes
    118. Pascal's Triangle
    loj3117 IOI2017 接线 wiring 题解
    题解 NOI2019 序列
    题解 省选联考2020 组合数问题
    题解 Educational Codeforces Round 90 (Rated for Div. 2) (CF1373)
  • 原文地址:https://www.cnblogs.com/wishyouhappy/p/3838541.html
Copyright © 2011-2022 走看看