问题
I have a black and white image and i would like to paint it with the color i picked without removing the image. When i start to paint its painting the image borders either.
I would like to still have the image over there and paint just the "background" of a PNG image just like this script: "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>
回答1:
A very different solution comes to mind now that canvas has come to stage two: more blending modes, which simplifies the whole process:
- Draw the image, make sure background is white, lines black ("dah" perhaps, but important)
- Change composite (blending) mode to
multiply
- note: does not work yet in IE (incl. v.11). - Draw your heart out on top of it; create master pieces such as this:
(uh!... a rave-duck)
This mode will also act more like real paper and markers, ie. subtractive light when blending the colors together.
Here you don't need to worry about alpha channel/transparency as it uses only blending mode (no composition but the regular source-over, takes place).
You can further adjust blending ratio by adjusting the global alpha (note also that you will get those overlaps on the line connections - not covered in this answer, but one possible solution here (overlap), and for recording points here).
ctx.globalAlpha = 0.75; // example, set/adjust before drawing
Demo
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}
<canvas width=400 height=400></canvas>
回答2:
Redraw the outline on top whenever the user paints an added color
This is probably the easiest way of always having the original outline above the painted canvas. If you have a colored background outside the original image then redrawing the original image will eliminate any coloring outside the outline.
An interesting alternative: Use Compositing to color behind the original outline
If you want your new colorings to always be behind the original outline, you can set context.globalCompositeOperation='destination-over'
and then all your new colorings will always be drawn behind the existing outline.
Note: This technique works by only coloring new pixels where the new pixel doesn't overlap any existing colored pixel.
So, to use this technique, you must make these changes on your outline image:
Use an outline that is not already filled with a color. William Malone's nice work uses a duck that is already filled with white. You would have to remove that interior white color.
Fill the background outside the outline with a color (not transparent)
I include a modified image of William Malone's duck outline that's properly set up to use compositing to always "draw within the lines". Although you can't see it from the image on a white background...
the background outside the image is colored white
and the interior of the duck is transparent.
Here's a quack example, I mean quick example (pun intended!):
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>
来源:https://stackoverflow.com/questions/29241548/paint-an-image-without-removing-it-on-html5-canvas