【问题标题】:Paint an image without removing it on html5 canvas在 html5 画布上绘制图像而不将其删除
【发布时间】:2015-03-25 10:14:57
【问题描述】:

我有一张黑白图像,我想用我选择的颜色绘制它而不删除图像。当我开始画它的画时,图像的边框也是如此。

我想保留图像并只绘制 PNG 图像的“背景”,就像这个脚本一样:“http://www.williammalone.com/articles/create-html5-canvas-javascript-drawing-app/#demo-sizes”

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
<script src="js/jquery-1.11.2.min.js"></script>

<!--http://perfectionkills.com/exploring-canvas-drawing-techniques/-->
<script type="text/javascript">


var isFirefox = typeof InstallTrigger !== 'undefined';

var CanvasLogBook = function() {
    this.index = 0;
    this.logs = [];
    this.logDrawing();
};
CanvasLogBook.prototype.sliceAndPush = function(imageObject) {
    var array;
    if (this.index == this.logs.length-1) {
        this.logs.push(imageObject);
        array = this.logs;
    } else {
        var tempArray = this.logs.slice(0, this.index+1);
        tempArray.push(imageObject);
        array = tempArray;
    }
    if (array.length > 1) {
        this.index++;
    }
    return array;
};
CanvasLogBook.prototype.logDrawing = function() { 
    if (isFirefox) {
        var image = new Image();

        image.src = document.getElementById('can').toDataURL();
        //var canvas = document.getElementsByClassName("can");
        //image.src = canvas.toDataURL();

        this.logs = this.sliceAndPush(image);
    } else {
        var imageData = document.getElementById('can').toDataURL();
        this.logs = this.sliceAndPush(imageData);
    }
};
CanvasLogBook.prototype.undo = function() {
    ctx.clearRect(0, 0, $('#can').width(), $('#can').height());
    if (this.index > 0) {
        this.index--;
        this.showLogAtIndex(this.index);
    }
};
CanvasLogBook.prototype.redo = function() {
    if (this.index < this.logs.length-1) {
        ctx.clearRect(0, 0, $('#can').width(), $('#can').height());
        this.index++;
        this.showLogAtIndex(this.index);
    }
};
CanvasLogBook.prototype.showLogAtIndex = function(index) {
    ctx.clearRect(0, 0, $('#can').width(), $('#can').height());
    if (isFirefox) {
        var image = this.logs[index];
        ctx.drawImage(image, 0, 0);
    } else {
        var image = new Image();
        image.src = this.logs[index];
        ctx.drawImage(image, 0, 0);
    }
};




$( document ).ready(function() {

    var canvasLogBook = new CanvasLogBook();

    $( "#undo" ).click(function() {
        canvasLogBook.undo();
    });

    $( "#redo" ).click(function() {
        canvasLogBook.redo();
    });


    $( "canvas" ).click(function() {
        canvasLogBook.logDrawing();
    }); 

}); 




var img = document.createElement("img");        // Create a <button> element
img.setAttribute('src','img.png');

var canvas, ctx, flag = false,
    prevX = 0,
    currX = 0,
    prevY = 0,
    currY = 0,
    dot_flag = false;

var x = "black",
    y = 10;

function init() {
    canvas = document.getElementById('can');
    ctx = canvas.getContext("2d");
    w = canvas.width;
    h = canvas.height;


   canvas.addEventListener("mousemove", function (e) {
        findxy('move', e)
    }, false);
    canvas.addEventListener("mousedown", function (e) {
        findxy('down', e)
    }, false);
    canvas.addEventListener("mouseup", function (e) {
        findxy('up', e)
    }, false);
    canvas.addEventListener("mouseout", function (e) {
        findxy('out', e)
    }, false);


    ctx.drawImage(img,90, 20); //desenho a img no começo


}

function color(obj) {


    var cor_sombra_selecionado = $('input[name=sombra]:checked').val();
    if ( (cor_sombra_selecionado == '') || (cor_sombra_selecionado =='nenhuma') ){
        ctx.shadowColor = obj.id;
    } else {
        ctx.shadowColor = cor_sombra_selecionado;
    }


    switch (obj.id) {
        case "green":
            x = "green";
            break;
        case "blue":
            x = "blue";
            break;
        case "red":
            x = "red";
            break;
        case "yellow":
            x = "yellow";
            break;
        case "orange":
            x = "orange";
            break;
        case "black":
            x = "black";
            break;
        case "white":
            x = "white";
            break;
    }
    if (x == "white") y = 10;
    else y = 10;

}

function sombra_funcao(cor) {

    var cor_sombra_selecionado = $('input[name=sombra]:checked').val();
    console.log(cor_sombra_selecionado + ' cliquei na cor da sombra');
    if ( (cor_sombra_selecionado == '') || (cor_sombra_selecionado =='nenhuma') ){
        ctx.shadowColor = x;
    } else {
        ctx.shadowColor = cor_sombra_selecionado;
    }



}

function draw() {

    var cor_sombra_selecionado = $('input[type=radio]:checked').val();
    console.log(cor_sombra_selecionado + ' cliquei na cor');
    if ( (cor_sombra_selecionado == '') || (cor_sombra_selecionado =='nenhuma') ){
        ctx.shadowColor = x;
    } else {
        ctx.shadowColor = cor_sombra_selecionado;
    }


    ctx.beginPath();
    ctx.lineCap = "round";
    ctx.lineJoin    = 'round';
    ctx.shadowBlur = 4;


    ctx.moveTo(prevX, prevY);
    ctx.lineTo(currX, currY);
    ctx.strokeStyle = x;
    ctx.lineWidth = y;
    ctx.stroke();

    console.log("dsa");
    ctx.closePath();
}

function erase() {
    var m = confirm("Want to clear");
    if (m) {
        ctx.clearRect(0, 0, w, h);
        document.getElementById("canvasimg").style.display = "none";
    }
    ctx.drawImage(img,90, 20); //desenho o bg
}

function save() {

    document.getElementById("canvasimg").style.border = "2px solid";
    var dataURL = canvas.toDataURL();
    document.getElementById("canvasimg").src = dataURL;
    document.getElementById("canvasimg").style.display = "inline";
}

function findxy(res, e) {
    if (res == 'down') {
        prevX = currX;
        prevY = currY;
        currX = e.clientX - canvas.offsetLeft;
        currY = e.clientY - canvas.offsetTop;

        flag = true;
        dot_flag = true;
        if (dot_flag) {
            ctx.strokeStyle = x;

            ctx.beginPath();
            ctx.fillStyle = x;
            //ctx.fillRect(currX, currY, 10, 10,10,10);
            ctx.arc(currX, currY,4,0,2*Math.PI);
            //ctx.arc(currX, currY,2,2,2,2*Math.PI);
            ctx.lineCap = "round";
            ctx.lineJoin = 'round';
            //ctx.stroke();
            dot_flag = false;
        }
    }
    if (res == 'up' || res == "out") {
        flag = false;
    }
    if (res == 'move') {
        if (flag) {
            prevX = currX;
            prevY = currY;
            currX = e.clientX - canvas.offsetLeft;
            currY = e.clientY - canvas.offsetTop;
            draw();
        }
    }
}

/*$( document ).ready(function() {

(function($) {
  var tool;


  var history = {
    redo_list: [],
    undo_list: [],
    saveState: function(canvas, list, keep_redo) {
      keep_redo = keep_redo || false;
      if(!keep_redo) {
        this.redo_list = [];
      }

      (list || this.undo_list).push(canvas.toDataURL());   
    },
    undo: function(canvas, ctx) {
      this.restoreState(canvas, ctx, this.undo_list, this.redo_list);
    },
    redo: function(canvas, ctx) {
      this.restoreState(canvas, ctx, this.redo_list, this.undo_list);
    },
    restoreState: function(canvas, ctx,  pop, push) {
      if(pop.length) {
        this.saveState(canvas, push, true);
        var restore_state = pop.pop();
        var img = new Element('img', {'src':restore_state});
        img.onload = function() {
          ctx.clearRect(0, 0, 600, 400);
          ctx.drawImage(img, 0, 0, 600, 400, 0, 0, 600, 400);  
        }
      }
    }
  };


  });


    $( "#undo" ).click(function() {
    console.log("das");
        history.undo(canvas, ctx);
    });








}); //ready  */



</script>

</head>

<body onload="init()">

<div class="container">

    <div class="row">
    <div class="col-md-12"><h3>Desenhar</h3></div>
    </div>

    <div class="row">

    </div>

    <div class="row">



        <canvas id="can" class='teste' width="500" height="400" style="border:2px solid;float:right"></canvas>

        <div class="col-md-6">
            <div class="col-md-12">
                <h3>Cores</h3>
                <div class="col-md-2"> <label style="background:green;"><input type='radio' name='cor' id='green' onclick="color(this)">Verde</label>   </div>
                <div class="col-md-2"> <label style="background:blue;"><input type='radio' name='cor' id='blue' onclick="color(this)">Azul</label>   </div>
                <div class="col-md-2"> <label style="background:red;"><input type='radio' name='cor' id='red' onclick="color(this)">Vermelho</label>   </div>
                <div class="col-md-2"> <label style="background:yellow;"><input type='radio' name='cor' id='yellow' onclick="color(this)">Amarelo</label>   </div>
                <div class="col-md-2"> <label style="background:orange;"><input type='radio' name='cor' id='orange' onclick="color(this)">Laranja</label>   </div>
                <div class="col-md-2"> <label style="background:white;"><input type='radio' name='cor' id='white' onclick="color(this)">Branco</label>   </div>
            </div>
        </div>

        <div class="col-md-6">
            <div class="col-md-12">
                <h3>Sombras</h3>
                <div class="col-md-2"> <label style="background:green;"><input type='radio' name='sombra' value='green' onclick="sombra_funcao('green')">Verde</label>   </div>
                <div class="col-md-2"> <label style="background:blue;"><input type='radio' name='sombra' value='blue' onclick="sombra_funcao('blue')">Azul</label>   </div>
                <div class="col-md-2"> <label style="background:red;"><input type='radio' name='sombra' value='red' onclick="sombra_funcao('red')">Vermelho</label>   </div>
                <div class="col-md-2"> <label style="background:yellow;"><input type='radio' name='sombra' value='yellow' onclick="sombra_funcao('yellow')">Amarelo</label>   </div>
                <div class="col-md-2"> <label style="background:orange;"><input type='radio' name='sombra' value='orange' onclick="sombra_funcao('orange')">Laranja</label>   </div>
                <div class="col-md-2"> <label style="background:white;"><input type='radio' name='sombra' value='nenhuma' onclick="sombra_funcao('nenhuma')" checked>Nenhuma</label>   </div>
            </div>
        </div>

    </div>


    <div class="row">   
        <div class="col-md-6">
            <img id="canvasimg" style="position:relative;float:left;border:1px solid #000" style="display:none;">
            <input type="button" value="save" id="btn" size="30" onclick="save()" style="">
            <input type="button" value="clear" id="clr" size="23" onclick="erase()" style="">
            <span class="controller" id="undo">desfazer </span>  
            <span class="controller" id="redo">redo</span>
        </div>


    </div>

</div>

</body>
</html>

【问题讨论】:

    标签: html canvas paint tint


    【解决方案1】:

    现在想到了一个非常不同的解决方案,画布已经进入第二阶段:更多混合模式,这简化了整个过程:

    • 绘制图像,确保背景为白色,线条为黑色(可能是“dah”,但很重要)
    • 将复合(混合)模式更改为 multiply - 注意:在 IE(包括 v.11)中尚不可用。
    • 在上面画出你的心;创作这样的杰作:


    (呃!...一个狂欢的鸭子)

    这种模式也将更像真正的纸和记号笔,即。将颜色混合在一起时的减色光。

    在这里您无需担心 Alpha 通道/透明度,因为它仅使用 混合 模式(不进行合成,但会进行常规的源切换)。

    您可以通过调整全局 alpha 来进一步调整混合比率(另请注意,您会在线路连接上获得这些重叠 - 此答案未涵盖,但 one possible solution here (overlap) 和 recording points here)。

    ctx.globalAlpha = 0.75;   // example, set/adjust before drawing
    

    演示

    var img = new Image;
    img.onload = setup;
    img.src = "http://i.stack.imgur.com/xL8it.png";
    
    function setup() {
        var canvas = document.querySelector("canvas"),
            ctx = canvas.getContext("2d"),
            lastPos, isDown = false;
    
        ctx.drawImage(this, 0, 0, canvas.width, canvas.height);  // draw duck        
        ctx.lineCap = "round";                                   // make lines prettier
        ctx.lineWidth = 16;
        ctx.globalCompositeOperation = "multiply";               // KEY MODE HERE
        
        canvas.onmousedown = function(e) {
            isDown = true;
            lastPos = getPos(e);
            ctx.strokeStyle = "hsl(" + (Math.random() * 360) + ", 100%, 85%)";
        };
        window.onmousemove = function(e) {
            if (!isDown) return;
            var pos = getPos(e);
            ctx.beginPath();
            ctx.moveTo(lastPos.x, lastPos.y);
            ctx.lineTo(pos.x, pos.y);
            ctx.stroke();
            lastPos = pos;
        };
        window.onmouseup = function(e) {isDown = false};
        
        function getPos(e) {
            var rect = canvas.getBoundingClientRect();
            return {x: e.clientX - rect.left, y: e.clientY - rect.top}
        }
    }
    canvas{border:1px solid #000;cursor:crosshair}
    &lt;canvas width=400 height=400&gt;&lt;/canvas&gt;

    【讨论】:

    • 单色效果很好,但是当我尝试使用多种颜色时,新颜色会呈现在旧颜色后面,有什么解决方案吗?由于这个原因,我的橡皮擦不工作了,谢谢 :)
    【解决方案2】:

    每当用户绘制添加的颜色时,在顶部重新绘制轮廓

    这可能是始终在画布上方保留原始轮廓的最简单方法。如果您在原始图像之外有彩色背景,则重绘原始图像将消除轮廓之外的任何颜色。

    一个有趣的替代方法:使用合成在原始轮廓后面着色

    如果您希望新颜色始终位于原始轮廓之后,您可以设置context.globalCompositeOperation='destination-over',然后您的所有新颜色将始终绘制在现有轮廓之后。

    注意:此技术仅通过对新像素不与任何现有彩色像素重叠的新像素着色来工作。

    因此,要使用此技术,您必须对轮廓图像进行以下更改:

    • 使用尚未填充颜色的轮廓。威廉马龙的好作品使用了一只已经填满白色的鸭子。您必须去除内部的白色。

    • 用颜色填充轮廓外的背景(不透明)

    我添加了威廉马龙鸭子轮廓的修改图像,该图像已正确设置为使用合成始终“在线条内绘制”。虽然你不能从白色背景的图像中看到它......

    • 图片外的背景为白色

    • 而且鸭子的内部是透明的。

    这是一个庸医的例子,我的意思是简单的例子(双关语!):

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    var cw=canvas.width;
    var ch=canvas.height;
    
    function reOffset(){
      var BB=canvas.getBoundingClientRect();
      offsetX=BB.left;
      offsetY=BB.top;        
    }
    var offsetX,offsetY;
    reOffset();
    window.onscroll=function(e){ reOffset(); }
    
    var isDown=false;
    var startX,startY;
    
    var img=new Image();img.onload=start;img.src="https://dl.dropboxusercontent.com/u/139992952/multple/duck.png";
    function start(){
    
      cw=canvas.width=img.width;
      ch=canvas.height=img.height;
    
      ctx.fillStyle='gold';
    
      ctx.drawImage(img,0,0);
    
      ctx.globalCompositeOperation='destination-over';
    
      $("#canvas").mousedown(function(e){handleMouseDown(e);});
      $("#canvas").mousemove(function(e){handleMouseMove(e);});
      $("#canvas").mouseup(function(e){handleMouseUp(e);});
      $("#canvas").mouseout(function(e){handleMouseOut(e);});
    
    
    }
    
    
    
    function handleMouseDown(e){
      // tell the browser we're handling this event
      e.preventDefault();
      e.stopPropagation();
    
      // Put your mousedown stuff here
      isDown=true;
    }
    
    function handleMouseUp(e){
      // tell the browser we're handling this event
      e.preventDefault();
      e.stopPropagation();
    
      // Put your mouseup stuff here
      isDown=false;
    }
    
    function handleMouseOut(e){
      // tell the browser we're handling this event
      e.preventDefault();
      e.stopPropagation();
    
      // Put your mouseOut stuff here
      isDown=false;
    }
    
    function handleMouseMove(e){
      if(!isDown){return;}
      // tell the browser we're handling this event
      e.preventDefault();
      e.stopPropagation();
    
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
    
      ctx.beginPath();
      ctx.arc(mouseX,mouseY,10,0,Math.PI*2);
      ctx.closePath();
      ctx.fill();
    
    }
    body{ background-color: ivory; }
    #canvas{border:1px solid red;}
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <canvas id="canvas" width=300 height=300></canvas>

    【讨论】:

    • 重要且希望是建设性的,注意使用destination-over - 假设您用蓝色绘画,但随后想在蓝色之上使用绿色(仍在黑色轮廓后面)。现在绿色也将涂在蓝色的后面,而不是在蓝色的上面(并不是说绿色鸭子比蓝色鸭子更好)。对于单一颜色当然没有问题(如在现场示例中),但对于多种颜色选项要记住一些事情(另请参阅重复指针以获取解决方案)。
    • 是的,多种颜色(以及跨越 2 个以上轮廓区域的意外绘制)是一个需要解决的问题。 ;-) 也许威廉马龙使用洪水填充的原始解决方案是一个成功的解决方案。 :-/
    • 洪水填充效果很好,但这种类型的图纸会带走乐趣(imo)。 :^)
    • 你们太棒了!谢谢!现在好啦。但是...关于多种颜色?它只是不覆盖第一个绘制的颜色。我该如何解决?我该如何处理 Ken 写的重复指针?
    • 另一件事是关于色调。例如,我如何为鸭子头部的所有区域着色?
    猜你喜欢
    • 2016-08-22
    • 1970-01-01
    • 2020-04-14
    • 1970-01-01
    • 1970-01-01
    • 2015-01-27
    • 2021-03-02
    • 2013-03-28
    • 1970-01-01
    相关资源
    最近更新 更多