I want to draw pattern on an image with canvas on click - javascript

i want to draw pattern on a image with canvas on click you can understand more with the provided images
here is what end result i want
http://i.imgur.com/wnH2Vxu.png
But i am having this blurred line
http://i.imgur.com/HXF1rTv.png
i am using following code
(
function() {
var canvas = document.querySelector('#canvas');
var ctx = canvas.getContext('2d');
var sketch = document.querySelector('#sketch');
var sketch_style = getComputedStyle(sketch);
canvas.width = parseInt(sketch_style.getPropertyValue('width'));
canvas.height = parseInt(sketch_style.getPropertyValue('height'));
var mouse = {x: 0, y: 0};
var last_mouse = {x: 0, y: 0};
/* Mouse Capturing Work */
canvas.addEventListener('mousemove', function(e) {
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
mouse.x = e.pageX - this.offsetLeft;
mouse.y = e.pageY - this.offsetTop;
}, false);
var texture = document.getElementById("texture");
pFill = ctx.createPattern(texture, "repeat");
ctx.strokeStyle = pFill;
/* Drawing on Paint App */
ctx.lineWidth = 12;
ctx.lineJoin = 'square';
ctx.lineCap = 'square';
canvas.addEventListener('mousedown', function(e) {
canvas.addEventListener('mousemove', onPaint, false);
}, false);
canvas.addEventListener('mouseup', function() {
canvas.removeEventListener('mousemove', onPaint, false);
}, false);
var onPaint = function() {
ctx.beginPath();
ctx.moveTo(last_mouse.x, last_mouse.y);
ctx.lineTo(mouse.x, mouse.y);
ctx.closePath();
ctx.stroke();
};
}()
);
$( document ).ready(function() {
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var img = document.getElementById("scream");
ctx.drawImage(img,10,10);
});

Method with stroke pattern will not work here. Because this pattern position is fixed. So when your line is not exactly horizontal or vertical you will have a distorted image instead of a chain of accurate balls.
Well I think the whole approach should be reconsidered.
User should create a path like in photoshop, which is visible as a temporary line. At first it can easily be a polyline, where vertices are pointed by clicks. And when the path is confirmed (by double click for example) you should remove the temporary line and put the balls along the path with a given step. In this case you can handle all turns and regularity distortions.
This is quite a bunch of work, but the task is in reality much more complex than it seems to be.
As a quick workaround you can try a simple approach. Drop a ball each time distance from the previous drop is more than double radius (for example) of the ball..
var lastBall = {x: null, y : null};
function drawBall(center){
ctx.beginPath();
ctx.arc(center.x, center.y, 10, 0, 2 * Math.PI, false);
ctx.fillStyle = 'orange';
ctx.fill();
}
function distance(a, b){
return Math.sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) );
}
var onPaint = function() {
if(!lastBall.x || distance(lastBall, mouse) > 25 ){
drawBall(mouse);
lastBall.x = mouse.x;
lastBall.y = mouse.y;
}
The code is rough you should define proper variables for color and radius, or perhaps replace it with your pattern, but you can get the idea
Fiddle

Related

Erasing only paticular element of canvas in JS

I want to create something like scratch card.
I created a canvas and added text to it.I than added a box over the text to hide it.Finally write down the code to erase(scratch) that box.
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.font = "30px Arial";
ctx.fillText("Hello World",10,50);
ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle='red';
ctx.fillRect(0,0,500,500);
function myFunction(event) {
var x = event.touches[0].clientX;
var y = event.touches[0].clientY;
document.getElementById("demo").innerHTML = x + ", " + y;
ctx.globalCompositeOperation = 'destination-out';
ctx.arc(x,y,30,0,2*Math.PI);
ctx.fill();
}
But the problem is it delete the text also.
How could I only delete that box not the text?
Canvas context keeps only one drawing state, which is the one rendered. If you modify a pixel, it won't remember how it was before, and since it has no built-in concept of layers, when you clear a pixel, it's just a transparent pixel.
So to achieve what you want, the easiest is to build this layering logic yourself, e.g by creating two "off-screen" canvases, as in "not appended in the DOM", one for the scratchable area, and one for the background that should be revealed.
Then on a third canvas, you'll draw both canvases every time. It is this third canvas that will be presented to your user:
var canvas = document.getElementById("myCanvas");
// the context that will be presented to the user
var main = canvas.getContext("2d");
// an offscreen one that will hold the background
var background = canvas.cloneNode().getContext("2d");
// and the one we will scratch
var scratch = canvas.cloneNode().getContext("2d");
generateBackground();
generateScratch();
drawAll();
// the events handlers
var down = false;
canvas.onmousemove = handlemousemove;
canvas.onmousedown = handlemousedown;
canvas.onmouseup = handlemouseup;
function drawAll() {
main.clearRect(0,0,canvas.width,canvas.height);
main.drawImage(background.canvas, 0,0);
main.drawImage(scratch.canvas, 0,0);
}
function generateBackground(){
background.font = "30px Arial";
background.fillText("Hello World",10,50);
}
function generateScratch() {
scratch.fillStyle='red';
scratch.fillRect(0,0,500,500);
scratch.globalCompositeOperation = 'destination-out';
}
function handlemousedown(evt) {
down = true;
handlemousemove(evt);
}
function handlemouseup(evt) {
down = false;
}
function handlemousemove(evt) {
if(!down) return;
var x = evt.clientX - canvas.offsetLeft;
var y = evt.clientY - canvas.offsetTop;
scratch.beginPath();
scratch.arc(x, y, 30, 0, 2*Math.PI);
scratch.fill();
drawAll();
}
<canvas id="myCanvas"></canvas>
Now, it could all have been done on the same canvas, but performance wise, it's probably not the best, since it implies generating an overly complex sub-path that should get re-rendered at every draw, also, it is not much easier to implement:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext('2d');
ctx.font = '30px Arial';
drawAll();
// the events handlers
var down = false;
canvas.onmousemove = handlemousemove;
canvas.onmousedown = handlemousedown;
canvas.onmouseup = handlemouseup;
function drawAll() {
ctx.globalCompositeOperation = 'source-over';
// first draw the scratch pad, intact
ctx.fillStyle = 'red';
ctx.fillRect(0,0,500,500);
// then erase with the currently being defined path
// see 'handlemousemove's note
ctx.globalCompositeOperation = 'destination-out';
ctx.fill();
// finally draw the text behind
ctx.globalCompositeOperation = 'destination-over';
ctx.fillStyle = 'black';
ctx.fillText("Hello World",10,50);
}
function handlemousedown(evt) {
down = true;
handlemousemove(evt);
}
function handlemouseup(evt) {
down = false;
}
function handlemousemove(evt) {
if(!down) return;
var x = evt.clientX - canvas.offsetLeft;
var y = evt.clientY - canvas.offsetTop;
// note how here we don't create a new Path,
// meaning that all the arcs are being added to the single one being rendered
ctx.moveTo(x, y);
ctx.arc(x, y, 30, 0, 2*Math.PI);
drawAll();
}
<canvas id="myCanvas"></canvas>
How could I only delete that box not the text?
You can't, you'll have to redraw the text. Once you've drawn the box over the text, you've obliterated it, it doesn't exist anymore. Canvas is pixel-based, not shape-based like SVG.

My mousedown function isn't drawing lines from the most recent point, but instead from the top left corner

http://codepen.io/PartTimeCoder/pen/qZJdPW?editors=0010
This is the link to my CodePen.
My HTML and the CSS are working fine. But the JavaScript isn't working the way I want it to. It should draw a line from the last point you clicked at.
The JavaScript is below -
var randomColor = function() {
return '#' + Math.random().toString(16).slice(2, 8);
}
var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d")
color = randomColor();
var height = window.innerHeight
var width = window.innerWidth
canvas.width = width
canvas.height = height
var mouse = {};
var circle_count = 10;
var circles = [];
var generate = function() {
for (var i = 0; i < circle_count; i++) {
circles.push(new circle());
}
}
setInterval(generate, 7500);
canvas.addEventListener('mousedown', mousePos, false);
canvas.addEventListener('touch', mousePos, false);
function mousePos(e) {
mouse.x = e.pageX;
mouse.y = e.pageY;
}
canvas.addEventListener("mousedown", function() {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
});
You need to save last clicked position before apply new one as on example:
codepen.io/themeler/pen/XdxboL?editors=0010
Each mousedown event calls ctx.moveTo(0, 0), which positions it in the upper left.
Move this code out of your mousedown event, and it works fine:
ctx.beginPath();
ctx.moveTo(0, 0);
CodePen
Change the mouse variable to set your starting point
var mouse = {x : 0, y : 0};
and then the event handler to update the mouse variable to the latest point
canvas.addEventListener('touch', stuff);
canvas.addEventListener("mousedown", stuff);
function stuff(e) {
ctx.beginPath();
ctx.moveTo(mouse.x, mouse.y);
ctx.lineTo(e.pageX, e.pageY);
ctx.stroke();
mouse = {x: e.pageX, y: e.pageY};
}
FIDDLE

JavaScript Canvas touch future with multiple eventListeners

I need to add a touch feature to the canvas app that I'm working on (multiple users white board), and I already read about the event listeners and the event.preventDefault(), but I can't understand how can I use two event listeners with 'DOMContentLoaded' and 'touchmove'. At this point I don't know if using multiple event listeners is the solution I need.
This is the code I'm using:
document.addEventListener("DOMContentLoaded", function() {
var mouse = {
click: false,
move: false,
pos: {x:0, y:0},
pos_prev: false
};
var canvas = document.getElementById('drawing');
var context = canvas.getContext('2d');
var width = 1280;
var height = 960;
var socket = io.connect();
var lineWidth = 1;
var shadowBlur = 1;
var shadowColor = "black";
var strokeStyle = "black";
canvas.width = width;
canvas.height = height;
canvas.onmousedown = function(e){ mouse.click = true; };
canvas.onmouseup = function(e){ mouse.click = false; };
canvas.onmousemove = function(e) {
mouse.pos.x = e.clientX / width;
mouse.pos.y = e.clientY / height;
mouse.move = true;
};
socket.on('draw_line', function (data) {
var line = data.line;
context.beginPath();
context.lineWidth = lineWidth;
context.shadowBlur = shadowBlur;
context.strokeStyle = strokeStyle;
context.lineJoin="round";
context.lineCap = "round";
context.moveTo(line[0].x * width, line[0].y * height);
context.lineTo(line[1].x * width, line[1].y * height);
context.stroke();
});
function mainLoop() {
if (mouse.click && mouse.move && mouse.pos_prev) {
socket.emit('draw_line', { line: [ mouse.pos, mouse.pos_prev ] });
mouse.move = false;
}
mouse.pos_prev = {x: mouse.pos.x, y: mouse.pos.y};
setTimeout(mainLoop, 50);
}
mainLoop();
});
I would really appreciate any help.
Thanks.
Are you encountering some sort of bug? Right now it appears that your code operates by listening for the mouse being clicked and the mouse being moved. If the mouse is pressed down, the mouse is being moved and the loop has run at least once (mouse.click && mouse.move && mouse.pos_prev) then it draws a line on the canvas element.

How can I stop HTML5 Canvas Ghosting?

I made a small program that:
changes the mouse cursor inside the canvas to a black square
gives the black square a nice trail that fades away over time (the point of the program)
Here's the code:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
canvas.style.cursor = 'none'; // remove regular cursor inside canvas
function getMousePos(canvas, e) {
var rect = canvas.getBoundingClientRect();
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
}
function fadeCanvas() {
ctx.save();
ctx.globalAlpha = 0.1; // the opacity (i.e. fade) being applied to the canvas on each function re-run
ctx.fillStyle = "#FFF";
ctx.fillRect(0, 0, canvas.width, canvas.height); // area being faded (whole canvas)
ctx.restore();
requestAnimationFrame(fadeCanvas); // animate at 60 fps
}
fadeCanvas();
function draw(e) {
var pos = getMousePos(canvas, e);
ctx.fillStyle = "black";
ctx.fillRect(pos.x, pos.y, 8, 8); // the new cursor
}
addEventListener('mousemove', draw, false);
Here's a live example: https://jsfiddle.net/L6j71crw/2/
Problem
However the trail does not fade away completely, and leaves a ghosting trail.
Q: How can I remove the ghosting trail?
I have tried using clearRect() in different ways, but it just clears the entire animation leaving nothing to display. At best it just removes the trail and only fades the square cursor alone, but it still doesn't make the cursor completely transparent when the fading process is completed. I have tried finding posts about it, but I found nothing that gave a definitive answer and—most importantly—no posts with a working example.
Any ideas?
Try having a list of positions, this won't leave a ghost trail!
my code:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var Positions = [];
var maxlength = 20;
canvas.style.cursor = 'none'; // remove regular cursor inside canvas
var V2 = function(x, y){this.x = x; this.y = y;};
function getMousePos(canvas, e) {
// ctx.clearRect(0, 0, canvas.width, canvas.height);
var rect = canvas.getBoundingClientRect();
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
}
function fadeCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(var e = 0; e != Positions.length; e++)
{
ctx.fillStyle = ctx.fillStyle = "rgba(0, 0, 0, " + 1 / e + ")";
ctx.fillRect(Positions[e].x, Positions[e].y, 8, 8);
}
if(Positions.length > 1)
Positions.pop()
//ctx.save();
//ctx.globalAlpha = 0.5; // the opacity (i.e. fade) being applied to the canvas on each function re-run
//ctx.fillStyle = "#fff";
//ctx.fillRect(0, 0, canvas.width, canvas.height); // area being faded (whole canvas)
//ctx.restore();
requestAnimationFrame(fadeCanvas); // animate at 60 fps
}
fadeCanvas();
function draw(e) {
var pos = getMousePos(canvas, e);
Positions.unshift(new V2(pos.x, pos.y));
if(Positions.length > maxlength)
Positions.pop()
//ctx.fillStyle = "black";
//ctx.fillRect(pos.x, pos.y, 8, 8); // the new cursor
}
addEventListener('mousemove', draw, false);
JSFiddle: https://jsfiddle.net/L6j71crw/9/
Edit: made the cursor constant.

JS variable not updating when button is clicked in HTML

I am making a Drawing/Canvas App on a webpage. I wanted to change the colours via buttons but the variables are not updating the colour. I debugged the code and noticed that it does update but the colour itself has not changed when drawing on the canvas.
HTML:
<div id="sketch">
<canvas id="paint"></canvas>
</div>
<button onClick="changecolour('blue')">Blue</button>
<button onClick="test()">DEBUG</button>
JavaScript:
var canvas = document.querySelector('#paint');
var ctx = canvas.getContext('2d');
var sketch = document.querySelector('#sketch');
var sketch_style = getComputedStyle(sketch);
canvas.width = parseInt(sketch_style.getPropertyValue('width'));
canvas.height = parseInt(sketch_style.getPropertyValue('height'));
var mouse = {x: 0, y: 0};
canvas.addEventListener('mousemove', function(e) {
mouse.x = e.pageX - this.offsetLeft;
mouse.y = e.pageY - this.offsetTop;
}, false);
var colour = "black";
function changecolour(choice){
colour = choice;
}
function test(click){
alert("You choose " + colour);
}
ctx.lineWidth = 5;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.strokeStyle = colour;
canvas.addEventListener('mousedown', function(e) {
ctx.beginPath();
ctx.moveTo(mouse.x, mouse.y);
canvas.addEventListener('mousemove', onPaint, false);
}, false);
canvas.addEventListener('mouseup', function() {
canvas.removeEventListener('mousemove', onPaint, false);
}, false);
var onPaint = function() {
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
};
You need to change the variable strokeStyle again.
function changecolour(choice){
colour = choice;
ctx.strokeStyle = colour;
}
You should affect the ctx.strokeStyle itself. When you first set his value, colour = 'black', which means you set it to black. It then stays black even tho you change the "colour" value. So in that case, you just have to do this:
function changecolour(choice){
ctx.strokeStyle = choice;
}
Hope that helps

Categories

Resources