So I made this canvas on which you can paint on. The problem is that when you erase your drawings it will also erase the background.
// SETTING ALL VARIABLES
var isMouseDown=false;
var canvas = document.createElement('canvas');
var body = document.getElementsByTagName("body")[0];
var ctx = canvas.getContext('2d');
var linesArray = [];
currentSize = 5;
var currentColor = "rgb(200,20,100)";
var currentBg = "white";
let newImage = new Image();
newImage.src = 'https://www.arnoldvanhooft.nl/wp-content/uploads/2019/06/ja-knop.png'
// INITIAL LAUNCH
newImage.onload = () => {
ctx.drawImage(newImage, 0, 0, 500, 500);
}
createCanvas();
// BUTTON EVENT HANDLERS
document.getElementById('canvasUpdate').addEventListener('click', function() {
createCanvas();
redraw();
});
document.getElementById('colorpicker').addEventListener('change', function() {
currentColor = this.value;
});
document.getElementById('bgcolorpicker').addEventListener('change', function() {
ctx.fillStyle = this.value;
ctx.fillRect(0, 0, canvas.width, canvas.height);
redraw();
currentBg = ctx.fillStyle;
});
document.getElementById('controlSize').addEventListener('change', function() {
currentSize = this.value;
document.getElementById("showSize").innerHTML = this.value;
});
document.getElementById('saveToImage').addEventListener('click', function() {
downloadCanvas(this, 'canvas', 'masterpiece.png');
}, false);
document.getElementById('eraser').addEventListener('click', eraser);
document.getElementById('clear').addEventListener('click', createCanvas);
document.getElementById('save').addEventListener('click', save);
document.getElementById('load').addEventListener('click', load);
document.getElementById('clearCache').addEventListener('click', function() {
localStorage.removeItem("savedCanvas");
linesArray = [];
console.log("Cache cleared!");
});
// REDRAW
function redraw() {
for (var i = 1; i < linesArray.length; i++) {
ctx.beginPath();
ctx.moveTo(linesArray[i-1].x, linesArray[i-1].y);
ctx.lineWidth = linesArray[i].size;
ctx.lineCap = "round";
ctx.strokeStyle = linesArray[i].color;
ctx.lineTo(linesArray[i].x, linesArray[i].y);
ctx.stroke();
}
}
// DRAWING EVENT HANDLERS
canvas.addEventListener('mousedown', function() {mousedown(canvas, event);});
canvas.addEventListener('mousemove',function() {mousemove(canvas, event);});
canvas.addEventListener('mouseup',mouseup);
// CREATE CANVAS
function createCanvas() {
canvas.id = "canvas";
canvas.width = parseInt(document.getElementById("sizeX").value);
canvas.height = parseInt(document.getElementById("sizeY").value);
canvas.style.zIndex = 8;
canvas.style.position = "absolute";
canvas.style.border = "1px solid";
ctx.fillStyle = currentBg;
ctx.fillRect(0, 0, canvas.width, canvas.height);
body.appendChild(canvas);
}
// DOWNLOAD CANVAS
function downloadCanvas(link, canvas, filename) {
link.href = document.getElementById(canvas).toDataURL();
link.download = filename;
}
// SAVE FUNCTION
function save() {
localStorage.removeItem("savedCanvas");
localStorage.setItem("savedCanvas", JSON.stringify(linesArray));
console.log("Saved canvas!");
}
// LOAD FUNCTION
function load() {
if (localStorage.getItem("savedCanvas") != null) {
linesArray = JSON.parse(localStorage.savedCanvas);
var lines = JSON.parse(localStorage.getItem("savedCanvas"));
for (var i = 1; i < lines.length; i++) {
ctx.beginPath();
ctx.moveTo(linesArray[i-1].x, linesArray[i-1].y);
ctx.lineWidth = linesArray[i].size;
ctx.lineCap = "round";
ctx.strokeStyle = linesArray[i].color;
ctx.lineTo(linesArray[i].x, linesArray[i].y);
ctx.stroke();
}
console.log("Canvas loaded.");
}
else {
console.log("No canvas in memory!");
}
}
// ERASER HANDLING
function eraser() {
currentSize = 50;
currentColor = ctx.fillStyle
}
// GET MOUSE POSITION
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
// ON MOUSE DOWN
function mousedown(canvas, evt) {
var mousePos = getMousePos(canvas, evt);
isMouseDown=true
var currentPosition = getMousePos(canvas, evt);
ctx.moveTo(currentPosition.x, currentPosition.y)
ctx.beginPath();
ctx.lineWidth = currentSize;
ctx.lineCap = "round";
ctx.strokeStyle = currentColor;
}
// ON MOUSE MOVE
function mousemove(canvas, evt) {
if(isMouseDown){
var currentPosition = getMousePos(canvas, evt);
ctx.lineTo(currentPosition.x, currentPosition.y)
ctx.stroke();
store(currentPosition.x, currentPosition.y, currentSize, currentColor);
}
}
// STORE DATA
function store(x, y, s, c) {
var line = {
"x": x,
"y": y,
"size": s,
"color": c
}
linesArray.push(line);
}
// ON MOUSE UP
function mouseup() {
isMouseDown=false
store()
}
.colorButtons {
display: block;
margin: 20px 0;
}
canvas {
cursor: crosshair;
}
div#sidebar {
position: absolute;
left: 0;
width: 150px;
padding: 20px 20px;
top: 0;
}
canvas#canvas {
left: 150px;
top: 45px;
}
.btn {
margin-bottom: 10px;
width: 100%;
}
input {
width: 100%;
margin-bottom: 10px;
}
.input-group {
margin-bottom: 10px;
}
.toolsButtons .btn {
width: 48%;
}
.sizeButtons .btn {
width: 48%;
}
.colorpicker {
background: transparent;
height: 40px;
}
<!-- using Bootstrap CSS because lazy to write 3 classes -->
<body>
<div id="sidebar">
<div class="colorButtons">
<h3>Colour</h3>
<input type="color" id="colorpicker" value="#c81464" class="colorpicker">
</div>
<div class="colorButtons">
<h3>Bg Color</h3>
<input type="color" value="#ffffff" id="bgcolorpicker" class="colorpicker">
</div>
<div class="toolsButtons">
<h3>Tools</h3>
<button id="eraser" class="btn btn-default">eraser</span></button>
<button id="clear" class="btn btn-danger"> <span class="glyphicon glyphicon-repeat" aria-hidden="true"></span></button>
</div>
<div class="buttonSize">
<h3>Size (<span id="showSize">5</span>)</h3>
<input type="range" min="1" max="50" value="5" step="1" id="controlSize">
</div>
<div class="canvasSize">
<h3>Canvas</h3>
<div class="input-group">
<span class="input-group-addon">X</span>
<input type="number" id="sizeX" class="form-control" placeholder="sizeX" value="800" class="size">
</div>
<div class="input-group">
<span class="input-group-addon">Y</span>
<input type="number" id="sizeY" class="form-control" placeholder="sizeY" value="800" class="size">
</div>
<input type="button" class="updateSize btn btn-success" value="Update" id="canvasUpdate">
</div>
<div class="Storage">
<h3>Storage</h3>
<input type="button" value="Save" class="btn btn-warning" id="save">
<input type="button" value="Load" class="btn btn-warning" id="load">
<input type="button" value="Clear" class="btn btn-warning" id="clearCache">
</div>
<div class="extra">
<h3>Extra</h3>
<a id="saveToImage" class="btn btn-warning">Download</a>
</div>
</div>
</body>
I have tried by adding the photo in a different way but that way it wouldn't be saved the right way. I also have tried changing layers with CSS and index but that also didn't work
Using layers
A canvas drawing app can use many canvases to define layers. Layers can include things like backgrounds, drawing layers, composite layers (multiply, screen, etc) and much more. Much the same as layers are used in apps like photoshop.
A bonus when using layers is that the immediate drawing state can be displayed without affecting the existing layers, as you can draw the pen on the output layer when the mouse button is not down. (see example)
To get the most from canvas layers you should become familiar with the many ctx.globalCompositeOperation modes.
The example uses the following ctx.globalCompositeOperation modes
"copy" copies pixels from source to destination including transparent pixels.
"source-over" (used in example draw mode) The default drawing mode. Copies pixels ignoring transparent pixels and blending semi transparent pixels.
"destination-out" (used in example erase mode) Removes pixels from the destination canvas where you draw opaque pixels, and partially removes pixels where you draw semi transparent pixels.
Performance
Even lowend devices can handle many canvas layers easily as long as you ensure that the canvas resolution does not exceed the device display size by many factors as performance is regulated by the availability of GPU RAM
You may be tempted to have the DOM handle the layer composition. It turns out that using the CanvasRenderingContext2D API to do layering is more efficient than letting the DOM handle it
Example
Below is a very basic drawing example. It uses 2 canvas layers, one for the background, and one for the drawing layer.
The background is loaded and then drawn to scale on the bg canvas.
When the mouse button is down the update function draws or erases to/from the drawing layer.
A 3rd canvas is used to show the result. This canvas is added to the DOM and the update function renders the layers to it as needed.
To save the result of the layers you can download the content of the 3rd canvas, or create a new canvas (if the display canvas size does not match the drawing size), draw the layers to it, and download its content.
Useage: Use mouse (left click) to draw / erase on drawing layer. Use button to toggle drawing mode (Draw / Erase)
;(()=>{
setTimeout(start, 0);
var ctx1, ctx2, ctx3;
const SIZE = 180;
const PEN_SIZE = 30;
function start() {
const button = tag("button", {textContent: "Draw", title: "Toggle erase / draw mode", className: "floatBtn"});
const canProps = {width: SIZE, height: SIZE};
ctx1 = tag("canvas", canProps).getContext("2d"); // BG layer
ctx2 = tag("canvas", canProps).getContext("2d"); // drawing layer
ctx3 = tag("canvas", canProps).getContext("2d"); // display canvas context
ctx2.lineWidth = ctx3.lineWidth = PEN_SIZE;
ctx2.lineCap = ctx3.lineCap = "round";
ctx2.lineJoin = ctx3.lineJoin = "round";
ctx2.strokeStyle = ctx3.strokeStyle = "BLUE";
append(BODY, ctx3.canvas, button);
// Load BG image and draw on bg canvas when loaded. Note bg is
// scaled to fit 180 by 180 canvas
const bgImg = new Image;
bgImg.src = "https://i.stack.imgur.com/C7qq2.png?s=256&g=1";
listener(bgImg, "load", () => (ctx1.drawImage(bgImg, 0, 0, 180, 180), mouse.update = true), {once: true});
listener(button, "click", () => {
mouse.draw = !mouse.draw; // Toggle drawing mode
button.textContent = mouse.draw ? "Draw" : "Erase";
});
mouse.update = true;
update();
}
function update() {
requestAnimationFrame(update)
if (!mouse.update) { return }
ctx3.globalCompositeOperation = "copy"; // to draw bg image
ctx3.drawImage(ctx1.canvas, 0 , 0);
if (mouse.lastX !== undefined) { // Avoid line from zero when mouse first over body
ctx3.globalCompositeOperation = "source-over"; // to draw drawing layer
if (mouse.button) { // draw on drawing layer if mouse down
ctx2.globalCompositeOperation = mouse.draw ? "source-over" : "destination-out";
ctx2.beginPath();
ctx2.lineTo(mouse.lastX, mouse.lastY);
ctx2.lineTo(mouse.x, mouse.y + 0.01); // Small 100th px offset
// ensures line is drawn
ctx2.stroke();
}
ctx3.drawImage(ctx2.canvas, 0 , 0);
if (!mouse.button) {
ctx3.strokeStyle = mouse.draw ? "BLUE" : "RED";
ctx3.beginPath();
ctx3.lineTo(mouse.lastX, mouse.lastY);
ctx3.lineTo(mouse.x, mouse.y + 0.01);
ctx3.stroke();
}
mouse.lastX = mouse.x;
mouse.lastY = mouse.y;
}
mouse.update = false;
}
const TAU = Math.PI * 2;
const DOC = document, BODY = DOC.body, assign = Object.assign;
const isArr = Array.isArray;
const tag = (tag, props = {}) => assign(DOC.createElement(tag), props);
const append = (el, ...sibs) => sibs.reduce((p, sib) => ((isArr(sib) ? append(p, ...sib) : p.appendChild(sib)), p), el);
const listener = (qe, name, call, opt = {}) => (qe.addEventListener(name, call, opt), qe);
const mouse = {x: 0, y: 0, button: false, lastX: undefined, lastY: undefined, draw: true, update: true}
function mouseEvents(e) {
mouse.update = true;
mouse.x = e.pageX;
mouse.y = e.pageY;
if (mouse.lastX === undefined) {
mouse.lastX = mouse.x;
mouse.lastY = mouse.y;
}
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down", "up", "move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));
})();
canvas { position: absolute; top: 0px; left: 0px; cursor: crosshair}
.floatBtn { position : absolute; top: 0px; left: 180px; cursor: pointer}
i am trying to create an image canvas where user can zoom into the image, the code which i got from here enter link description here, now i tried to add image inside it and i did the following code:
function draw(scale, translatePos) {
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
make_base(context);
}
function make_base(context) {
var base_image = new Image();
base_image.src = 'https://www.gstatic.com/webp/gallery3/1.sm.png';
base_image.onload = function() {
context.drawImage(base_image, 0, 0);
}
}
window.onload = function() {
var canvas = document.getElementById("myCanvas");
var translatePos = {
x: canvas.width / 2,
y: canvas.height / 2
};
var scale = 1.0;
var scaleMultiplier = 0.8;
var startDragOffset = {};
var mouseDown = false;
// add button event listeners
document.getElementById("plus").addEventListener("click", function() {
scale /= scaleMultiplier;
draw(scale, translatePos);
}, false);
document.getElementById("minus").addEventListener("click", function() {
scale *= scaleMultiplier;
draw(scale, translatePos);
}, false);
// add event listeners to handle screen drag
canvas.addEventListener("mousedown", function(evt) {
mouseDown = true;
startDragOffset.x = evt.clientX - translatePos.x;
startDragOffset.y = evt.clientY - translatePos.y;
});
canvas.addEventListener("mouseup", function(evt) {
mouseDown = false;
});
canvas.addEventListener("mouseover", function(evt) {
mouseDown = false;
});
canvas.addEventListener("mouseout", function(evt) {
mouseDown = false;
});
canvas.addEventListener("mousemove", function(evt) {
if (mouseDown) {
translatePos.x = evt.clientX - startDragOffset.x;
translatePos.y = evt.clientY - startDragOffset.y;
draw(scale, translatePos);
}
});
draw(scale, translatePos);
};
jQuery(document).ready(function() {
$("#wrapper").mouseover(function(e) {
$('#status').html(e.pageX + ', ' + e.pageY);
});
})
body {
margin: 0px;
padding: 0px;
}
#wrapper {
position: relative;
border: 1px solid #9C9898;
width: 578px;
height: 200px;
}
#buttonWrapper {
position: absolute;
width: 30px;
top: 2px;
right: 2px;
}
input[type="button"] {
padding: 5px;
width: 30px;
margin: 0px 0px 2px 0px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<body onmousedown="return false;">
<div id="wrapper">
<canvas id="myCanvas" width="578" height="200">
</canvas>
<div id="buttonWrapper">
<input type="button" id="plus" value="+"><input type="button" id="minus" value="-">
</div>
</div>
<h2 id="status">
0, 0
</h2>
</body>
however the image is not getting displayed inside the canvas, can anyone please tell me what could be wrong in here, thanks in advance
Your draw function never actually draws to the canvas. You get the canvas and context in the first 2 lines, but you need to call drawImage with the image to actually add it to the canvas itself.
I suspect you want to be calling make_base inside it like so:
function draw(scale, translatePos) {
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
make_base();
}
You also need to have the context in the same scope as you use it. At the moment, the variable context only exists inside the draw function and not the make_base function, so you can't access it from inside make_base.
You can pass it as a variable like so:
function draw(scale, translatePos) {
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
make_base(context);
}
function make_base(context) {
var base_image = new Image();
base_image.src = 'a2.jpg';
base_image.onload = function() {
context.drawImage(base_image, 0, 0);
}
}
Every time you want to change anything on an HTML canvas you need to call draw functions to change what's there.
I am working on a project where i need to create resizable rectangles on my image.
I found this codepen useful:
https://codepen.io/taye/pen/xEJeo
the problem is when i write same code in my codepen, its not working same. see
here
https://codepen.io/KushalParikh/pen/rJOeOy.
Can you please tell me what I am missing. I am new to javascript and svg.
my code
var svgCanvas = document.querySelector('svg'),
svgNS = 'http://www.w3.org/2000/svg',
rectangles = [];
function Rectangle (x, y, w, h, svgCanvas) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.stroke = 5;
this.el = document.createElementNS(svgNS, 'rect');
this.el.setAttribute('data-index', rectangles.length);
this.el.setAttribute('class', 'edit-rectangle');
rectangles.push(this);
this.draw();
svgCanvas.appendChild(this.el);
}
Rectangle.prototype.draw = function () {
this.el.setAttribute('x', this.x + this.stroke / 2);
this.el.setAttribute('y', this.y + this.stroke / 2);
this.el.setAttribute('width' , this.w - this.stroke);
this.el.setAttribute('height', this.h - this.stroke);
this.el.setAttribute('stroke-width', this.stroke);
}
interact('.edit-rectangle')
// change how interact gets the
// dimensions of '.edit-rectangle' elements
.rectChecker(function (element) {
// find the Rectangle object that the element belongs to
var rectangle = rectangles[element.getAttribute('data-index')];
// return a suitable object for interact.js
return {
left : rectangle.x,
top : rectangle.y,
right : rectangle.x + rectangle.w,
bottom: rectangle.y + rectangle.h
};
})
.inertia({
// don't jump to the resume location
// https://github.com/taye/interact.js/issues/13
zeroResumeDelta: true
})
.restrict({
// restrict to a parent element that matches this CSS selector
drag: 'svg',
// only restrict before ending the drag
endOnly: true,
// consider the element's dimensions when restricting
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
})
.draggable({
max: Infinity,
onmove: function (event) {
var rectangle = rectangles[event.target.getAttribute('data-index')];
rectangle.x += event.dx;
rectangle.y += event.dy;
rectangle.draw();
}
})
.resizable({
max: Infinity,
onmove: function (event) {
var rectangle = rectangles[event.target.getAttribute('data-index')];
rectangle.w = Math.max(rectangle.w + event.dx, 10);
rectangle.h = Math.max(rectangle.h + event.dy, 10);
rectangle.draw();
}
});
interact.maxInteractions(Infinity);
for (var i = 0; i < 5; i++) {
new Rectangle(50 + 100 * i, 80, 80, 80, svgCanvas);
}
svg {
width: 100%;
height: 240px;
background-color: #2e9;
-ms-touch-action: none;
touch-action: none;
}
.edit-rectangle {
fill: #92e;
stroke: black;
}
body { margin: 0; }
<svg>
</svg>
Indeed your pen is not working properly. You forgot to add interact.js to your pen as necessary javascript resource. Just go to Settings|Javascript and add something like https://c4d6f7d727e094887e93-4ea74b676357550bd514a6a5b344c625.ssl.cf2.rackcdn.com/interact-1.1.1.min.js
You need to import
https://c4d6f7d727e094887e93-4ea74b676357550bd514a6a5b344c625.ssl.cf2.rackcdn.com/interact-1.1.1.min.js
in your JS
I need to have a cursor to drag and take screenshot of dragged area on HTML webpage. I tried using HTML canvas but it takes screenshot of specific div not the selected region on HTML webpage.
The new html2canvas version 1 has width, height, x and y options.
You can make use of these options to achieve a cropping feature the Firefox's Screenshot's way.
document.onmousedown = startDrag;
document.onmouseup = endDrag;
document.onmousemove = expandDrag;
var dragging = false,
dragStart = {
x: 0,
y: 0
},
dragEnd = {
x: 0,
y: 0
};
function updateDragger() {
dragger.classList.add('visible');
var s = dragger.style;
s.top = Math.min(dragStart.y, dragEnd.y) + 'px';
s.left = Math.min(dragStart.x, dragEnd.x) + 'px';
s.height = Math.abs(dragStart.y - dragEnd.y) + 'px';
s.width = Math.abs(dragStart.x - dragEnd.x) + 'px';
}
function startDrag(evt) {
evt.preventDefault();
dragging = true;
dragStart.x = dragEnd.x = evt.clientX;
dragStart.y = dragEnd.y = evt.clientY;
updateDragger();
}
function expandDrag(evt) {
if (!dragging) return;
dragEnd.x = evt.clientX;
dragEnd.y = evt.clientY;
updateDragger();
}
function endDrag(evt) {
dragging = false;
dragger.classList.remove('visible');
// here is the important part
html2canvas(document.body, {
width: Math.abs(dragStart.x - dragEnd.x),
height: Math.abs(dragStart.y - dragEnd.y),
x: Math.min(dragStart.x, dragEnd.x),
y: Math.min(dragStart.y, dragEnd.y)
})
.then(function(c) {
document.body.appendChild(c);
});
dragStart.x = dragStart.y = dragEnd.x = dragEnd.y = 0;
}
* {
user-select: none;
}
#dragger {
position: fixed;
background: rgba(0, 0, 0, .5);
border: 1px dashed white;
pointer-events: none;
display: none;
}
#dragger.visible {
display: block;
}
canvas {
border: 1px solid;
}
<script src="https://github.com/niklasvh/html2canvas/releases/download/v1.0.0-alpha.1/html2canvas.js"></script>
<div id="wrapper">
<p> Drag to take a screenshot ...</p>
<img crossOrigin src="https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png" width="120" height="120">
</div>
<div id="dragger" tabindex></div>
I am trying to create new circles as my pen hovers on window.
I am having issues where I cannot add circles to the page. It just hovers around. How would i be able to modify my code to add circles as it hovers.
<!doctype html>
<html>
<head>
<title> JavaScript Environment: Project </title>
<meta charset="utf-8">
<style>
html, body {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
}
#canvas {
background-color: yellow;
width: 100%;
height: 100%;
}
.pen {
position: absolute;
background-color: lightblue;
width: 10px;
height: 10px;
border-radius: 5px;
}
</style>
<script>
window.onload = function() {
function Circle(x, y) {
this.x = x;
this.y = y;
}
var canvas = document.getElementById("canvas");
canvas.onmousedown = function() {
mouseDown();
};
canvas.onmouseup = function() {
mouseUp()
};
canvas.onmousemove = function() {
mouseMove(event)
};
function mouseDown (){
console.log ("mouse down");
}
function mouseUp (){
console.log ("mouse up");
}
function mouseMove(e) {
var canvas = document.getElementById("canvas");
var pen = document.createElement("div");
var x = e.clientX;
var y = e.clientY;
var coor = "Coordinates: (" + x + "," + y + ")";
pen.setAttribute("class", "pen");
pen.style.left = x + "px";
pen.style.top = y + "px";
document.getElementById("canvas").innerHTML = coor;
canvas.appendChild(pen);
addCircles(x, y);
console.log("location # " + x + " : " + y);
}
function addCircles(x, y) {
var canvas = document.getElementById("canvas");
var circle = document.createElement("div");
circle.setAttribute("class", "pen");
circle.style.left = x + "px";
circle.style.top = y + "px";
canvas.appendChild(circle);
}
canvas.addEventListener("mouseMove", mouseMove, false);
};
</script>
</head>
<body>
<div id="canvas"></div>
</body>
</html>
The problem is in the line document.getElementById("canvas").innerHTML = coor;
Try adding a span <span id="canvasText"></span> inside of your canvas div and then changing the above line to document.getElementById("canvasText").innerHTML = coor;.
As it stands, you "reset" the contents of the canvas every time the mouse moves, so the circles are instantly removed from it. Reset only the span inside the canvas to keep the circles around.