JS Custom Color Picker - javascript

Basically lol cant add videos here but with the default html color picker you can move the color picker dot if mouse is down off the colors box boundries and it moves agains the edge of the boundries but with mine it just stops moving and doesnt do good when i move the mouse fast.
I am trying to create a custom color picker where you have a slider of colors, then you have the box of the color selected and you can change the whiteness and darkness of it like this here (Like adobe xdcc color picker):
Adobe xdcc color picker example
[The code below creates this here][2]
You can drag the little color picker circle and it changes the selected color but when using canvas I can't have to color picker circle moving around the edges when the mouse leaves the canvas which i would like to change.
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
<script src="app.js" defer></script>
</head>
<body>
<h2>Let's Create a Color Picker</h2>
<div class="container">
<canvas id="color-picker"></canvas>
<div class="info">
<h3>Selected Color</h3>
<div class="selected"></div>
</div>
</div>
</body>
</html>
CSS:
html, body {
width: 100%;
height: 100%;
font-family: Oxygen, sans-serif;
text-align: center; }
.container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center; }
#color-picker {
border: .5px solid rgba(15, 15, 15, 0.2); }
.info {
width: 12em;
display: flex;
margin-left: 4em;
flex-direction: row;
justify-content: space-between; }
.selected {
width: 50px;
height: 50px;
border-radius: 100%;
border: 2px solid rgba(15, 15, 15, 0.2); }
JS:
class Picker {
constructor(target, width, height) {
this.target = target;
this.width = width;
this.height = height;
this.target.width = width;
this.target.height = height;
// Get context
this.context = this.target.getContext("2d");
// Circle
this.pickerCircle = { x: 10, y: 10, width: 10, height: 10 };
this.listenForEvents();
}
draw() {
}
build() {
let gradient = this.context.createLinearGradient(0, 0, this.width, 0);
//Color Stops
gradient.addColorStop(0, "rgb(255, 0, 0)");
gradient.addColorStop(0.15, "rgb(255, 0, 255)");
gradient.addColorStop(0.33, "rgb(0, 0, 255)");
gradient.addColorStop(0.49, "rgb(0, 255, 255)");
gradient.addColorStop(0.67, "rgb(0, 255, 0)");
gradient.addColorStop(0.84, "rgb(255, 255, 0)");
gradient.addColorStop(1, "rgb(255, 0, 0)");
//Fill it
this.context.fillStyle = gradient;
this.context.fillRect(0, 0, this.width, this.height);
//Apply black and white
gradient = this.context.createLinearGradient(0, 0, 0, this.height);
gradient.addColorStop(0, "rgba(255, 255, 255, 1)");
gradient.addColorStop(0.5, "rgba(255, 255, 255, 0)");
gradient.addColorStop(0.5, "rgba(0, 0, 0, 0)");
gradient.addColorStop(1, "rgba(0, 0, 0, 1)");
this.context.fillStyle = gradient;
this.context.fillRect(0, 0, this.width, this.height);
//Circle
this.context.beginPath();
this.context.arc(this.pickerCircle.x, this.pickerCircle.y, this.pickerCircle.width, 0, Math.PI * 2);
this.context.strokeStyle = "black";
this.context.stroke();
this.context.closePath();
}
listenForEvents() {
let isMouseDown = false;
const onMouseDown = (e) => {
this.build();
let currentX = e.clientX - this.target.offsetLeft;
let currentY = e.clientY - this.target.offsetTop;
this.pickerCircle.x = currentX;
this.pickerCircle.y = currentY;
isMouseDown = true;
}
const onMouseMove = (e) => {
if(isMouseDown) {
this.build();
let currentX = e.clientX - this.target.offsetLeft;
let currentY = e.clientY - this.target.offsetTop;
this.pickerCircle.x = currentX;
this.pickerCircle.y = currentY;
}
}
const onMouseUp = () => {
isMouseDown = false;
}
//Register
this.target.addEventListener("mousedown", onMouseDown);
this.target.addEventListener("mousemove", onMouseMove);
this.target.addEventListener("mousemove", () => this.onChangeCallback(this.getPickedColor()));
document.addEventListener("mouseup", onMouseUp);
}
getPickedColor() {
let imageData = this.context.getImageData(this.pickerCircle.x, this.pickerCircle.y, 1, 1);
return { r: imageData.data[0], g: imageData.data[1], b: imageData.data[2] };
}
onChange(callback) {
this.onChangeCallback = callback;
}
}
let picker = new Picker(document.getElementById("color-picker"), 250, 220); picker.build();
picker.onChange((color) => {
let selected = document.getElementsByClassName("selected")[0];
selected.style.backgroundColor = `rgb(${color.r}, ${color.g}, ${color.b})`; });
[1]: https://i.stack.imgur.com/MQsjs.png
[2]: https://i.stack.imgur.com/krEYa.png
Thanks in advance!

the solution is simple, dont use the if(isMouseDown){..}, because that will make your code very unresponsive, so what I did is that I declared a varable let i = 0; then on the onMouseDown, whenever that function is called, it will increment the value of i and I changed the onMouseMove from if(isMouseDown){..} to if(i % 2 === 0){..} heres my solution:
class Picker {
constructor(target, width, height) {
this.target = target;
this.width = width;
this.height = height;
this.target.width = width;
this.target.height = height;
//Get context
this.context = this.target.getContext("2d");
//Circle
this.pickerCircle = { x: 10, y: 10, width: 10, height: 10 };
this.listenForEvents();
}
draw() {
}
build() {
let gradient = this.context.createLinearGradient(0, 0, this.width, 0);
//Color Stops
gradient.addColorStop(0, "rgb(255, 0, 0)");
gradient.addColorStop(0.15, "rgb(255, 0, 255)");
gradient.addColorStop(0.33, "rgb(0, 0, 255)");
gradient.addColorStop(0.49, "rgb(0, 255, 255)");
gradient.addColorStop(0.67, "rgb(0, 255, 0)");
gradient.addColorStop(0.84, "rgb(255, 255, 0)");
gradient.addColorStop(1, "rgb(255, 0, 0)");
//Fill it
this.context.fillStyle = gradient;
this.context.fillRect(0, 0, this.width, this.height);
//Apply black and white
gradient = this.context.createLinearGradient(0, 0, 0, this.height);
gradient.addColorStop(0, "rgba(255, 255, 255, 1)");
gradient.addColorStop(0.5, "rgba(255, 255, 255, 0)");
gradient.addColorStop(0.5, "rgba(0, 0, 0, 0)");
gradient.addColorStop(1, "rgba(0, 0, 0, 1)");
this.context.fillStyle = gradient;
this.context.fillRect(0, 0, this.width, this.height);
//Circle
this.context.beginPath();
this.context.arc(this.pickerCircle.x, this.pickerCircle.y, this.pickerCircle.width, 0, Math.PI * 2);
this.context.strokeStyle = "black";
this.context.stroke();
this.context.closePath();
}
listenForEvents() {
let isMouseDown = true;
let i =0;
const onMouseDown = (e) => {
this.build();
isMouseDown = true
i++
let currentX = e.clientX - this.target.offsetLeft;
let currentY = e.clientY - this.target.offsetTop;
this.pickerCircle.x = currentX;
this.pickerCircle.y = currentY;
}
const onMouseMove = (e) => {
if(i%2 == 0) {
this.build();
let currentX = e.clientX - this.target.offsetLeft;
let currentY = e.clientY - this.target.offsetTop;
this.pickerCircle.x = currentX;
this.pickerCircle.y = currentY;
}
}
const onMouseUp = () => {
isMouseDown = false;
}
//Register
this.target.addEventListener("mousedown", onMouseDown);
this.target.addEventListener("mousemove", onMouseMove);
this.target.addEventListener("mousemove", () => this.onChangeCallback(this.getPickedColor()));
document.addEventListener("mouseup", onMouseUp);
}
getPickedColor() {
let imageData = this.context.getImageData(this.pickerCircle.x, this.pickerCircle.y, 1, 1);
return { r: imageData.data[0], g: imageData.data[1], b: imageData.data[2] };
}
onChange(callback) {
this.onChangeCallback = callback;
}
}
let picker = new Picker(document.getElementById("color-picker"), 250, 220); picker.build();
picker.onChange((color) => {
let selected = document.getElementsByClassName("selected")[0];
selected.style.backgroundColor = `rgb(${color.r}, ${color.g}, ${color.b})`; });
html, body {
width: 100%;
height: 100%;
font-family: Oxygen, sans-serif;
text-align: center; }
.container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center; }
#color-picker {
border: .5px solid rgba(15, 15, 15, 0.2); }
.info {
width: 12em;
display: flex;
margin-left: 4em;
flex-direction: row;
justify-content: space-between; }
.selected {
width: 50px;
height: 50px;
border-radius: 100%;
border: 2px solid rgba(15, 15, 15, 0.2); }
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
<script src="app.js" defer></script>
</head>
<body>
<h2>Let's Create a Color Picker</h2>
<div class="container">
<canvas id="color-picker"></canvas>
<div class="info">
<h3>Selected Color</h3>
<div class="selected"></div>
</div>
</div>
</body>
</html>

Related

Replicate Canvas resize in a Div

There's any way to replicate this scale behavior in a div ?
Here's an example of the behavior I wanted to reproduce within the div. I already have the movement part, and I just wanted to know about the resizing.
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"></script>
<style>
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;
}
</style>
<script>
function draw(scale, translatePos) {
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
// clear canvas
context.clearRect(0, 0, canvas.width, canvas.height);
context.save();
context.translate(translatePos.x, translatePos.y);
context.scale(scale, scale);
context.beginPath(); // begin custom shape
context.moveTo(-119, -20);
context.bezierCurveTo(-159, 0, -159, 50, -59, 50);
context.bezierCurveTo(-39, 80, 31, 80, 51, 50);
context.bezierCurveTo(131, 50, 131, 20, 101, 0);
context.bezierCurveTo(141, -60, 81, -70, 51, -50);
context.bezierCurveTo(31, -95, -39, -80, -39, -50);
context.bezierCurveTo(-89, -95, -139, -80, -119, -20);
context.closePath(); // complete custom shape
var grd = context.createLinearGradient(-59, -100, 81, 100);
grd.addColorStop(0, "#8ED6FF"); // light blue
grd.addColorStop(1, "#004CB3"); // dark blue
context.fillStyle = grd;
context.fill();
context.lineWidth = 5;
context.strokeStyle = "#0000ff";
context.stroke();
context.restore();
}
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);
});
})
</script>
</head>
<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>
</html>
I tried to use css scale, but it resizes the div too, I want to modify only the space and the elements inside it.

Issue with hovering over a button in JavaScript Canvas

```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<title>Paint, Inc.</title>
</head>
<body>
<div id="sideNav">
</div>
<canvas id="canvas"></canvas>
<script src="canvas.js"></script>
<button id="clear" title="Clear">X</button>
<section id="leftBumper"></section>
<section id="colorChoice">
<input id="color" type="color" value="#000000" />
<label id="colorLabel" for="color">Color</label>
</section>
<section id="strokeWeightChoice">
<input id="strokeWeight" type="range" min="1" max="51" step="5" value="1" list="tickmarks">
<label for="strokeWeight">Thickness</label>
<datalist id="tickmarks">
<option value="1"></option>
<option value="6"></option>
<option value="11"></option>
<option value="16"></option>
<option value="21"></option>
<option value="26"></option>
<option value="31"></option>
<option value="36"></option>
<option value="41"></option>
<option value="46"></option>
<option value="51"></option>
</datalist>
</section>
</body>
</html>
```
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#canvas {
border: 0.0001px solid white;
}
html {
overflow: hidden;
}
#clear {
position: absolute;
top: 0;
left: 0;
bottom: -10px;
width: 30px;
background: rgba(70, 70, 70, 0.32);
border: rgba(70, 70, 70, 0.32);
border-width: 5px;
font-weight: bold;
font-family: Arial, Helvetica, sans-serif;
font-size: 28px;
color: red;
}
#clear:hover {
transition: 0.4s;
background: rgba(20, 20, 20, 0.4);
cursor: pointer;
font-size: 32px;
}
#clear:focus {
outline: 0;
}
#colorChoice {
position: absolute;
bottom: 0.5rem;
right: 50%;
transform: translateX(50%);
font-family: Impact, Haettenschweiler, 'Arial Narrow Bold', sans-serif;
font-weight: bold;
}
#colorChoice:hover {
cursor: pointer;
}
#strokeWeightChoice {
position: absolute;
bottom: 3rem;
right: 50%;
transform: translateX(50%);
font-family: Impact, Haettenschweiler, 'Arial Narrow Bold', sans-serif;
font-weight: bold;
}
#strokeWeight {
width: 200px;
}
```
window.addEventListener("load", () => {
const canvas = document.querySelector("#canvas");
const ctx = canvas.getContext("2d");
const color = document.querySelector("#color");
const strokeWeight = document.querySelector("#strokeWeight");
//variables
const clearButton = document.querySelector("#clear");
let painting = false;
function startPosition(e) {
painting = true;
draw(e);
}
function finishedPosition() {
painting = false;
ctx.beginPath();
}
function draw(e) {
if (!painting) return;
ctx.lineCap = "round";
ctx.lineTo(e.clientX, e.clientY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(e.clientX, e.clientY);
}
function changeColor(e) {
const color = e.target.value;
ctx.strokeStyle = color;
}
function changeStrokeWeight(e) {
const strokeWeight = e.target.value;
ctx.lineWidth = strokeWeight;
}
//Event listeners
canvas.addEventListener("mousedown", startPosition);
canvas.addEventListener("mouseup", finishedPosition);
canvas.addEventListener("mousemove", draw);
color.addEventListener("input", changeColor);
strokeWeight.addEventListener("input", changeStrokeWeight);
//Buttons
clearButton.addEventListener("click", clearCanvas);
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
});
window.addEventListener("resize", resizeCanvas);
function resizeCanvas() {
//Resizing
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
}
resizeCanvas();
On my canvas whenever I draw while hovering over a button then moving my cursor back to the canvas causes me to draw without pressing until I press again. Could someone please tell me how to fix this glitch. This glitch occurs with my clear button, thickness slider, and my color changer. In addition how do I add something to stop drawing on the clear button.
Let me know if the following change to the javascript will work for you -
When draw() is called by a Mouse Move event it won't check if painting is set to true anymore rather it will check the status of the Mouse Event to see if they are holding down the left click that way when you go off the canvas and hover over one of the buttons and come back onto the canvas it will check to see if you have the left click held down and draw if that's true
Fiddle here if it helps
You can see more about those which properties here
window.addEventListener("load", () => {
const canvas = document.querySelector("#canvas");
const ctx = canvas.getContext("2d");
const color = document.querySelector("#color");
const strokeWeight = document.querySelector("#strokeWeight");
//variables
const clearButton = document.querySelector("#clear");
let painting = false;
function startPosition(e) {
painting = true;
draw(e);
}
function finishedPosition() {
painting = false;
ctx.beginPath();
}
function draw(e) {
//if (!painting) return;
if(e.which == 1){
ctx.lineCap = "round";
ctx.lineTo(e.clientX, e.clientY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(e.clientX, e.clientY);
}else{
finishedPosition();
}
}
function changeColor(e) {
const color = e.target.value;
ctx.strokeStyle = color;
}
function changeStrokeWeight(e) {
const strokeWeight = e.target.value;
ctx.lineWidth = strokeWeight;
}
//Event listeners
canvas.addEventListener("mousedown", startPosition);
canvas.addEventListener("mouseup", finishedPosition);
canvas.addEventListener("mousemove", draw);
color.addEventListener("input", changeColor);
strokeWeight.addEventListener("input", changeStrokeWeight);
//Buttons
clearButton.addEventListener("click", clearCanvas);
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
});
window.addEventListener("resize", resizeCanvas);
function resizeCanvas() {
//Resizing
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
}
resizeCanvas();

Unable to get proper position in Canvas using ES6 (why isn't this code working properly?)

I am trying make an paint app using ES6. But i am not getting proper position and line in canvas.
This line is not drawn in correct position, like top-left is formed when i click and from 0,0 corner of canvas.
As you can see Line is not starting from the point Cursor is pointing and this difference increases as we move from TOP-LEFT cornor to BOTTOM-RIGHT cornor.
const TOOL_LINE = 'line';
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class Paint {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.context = canvas.getContext("2d");
}
set activeTool(tool) {
this.tool = tool;
}
init() {
this.canvas.onmousedown = e => this.onMouseDown(e);
}
onMouseDown(e) {
this.saveData = this.context.getImageData(0, 0, this.canvas.clientWidth, this.canvas.clientHeight);
this.canvas.onmousemove = e => this.onMouseMove(e);
document.onmouseup = e => this.onMouseUp(e);
this.startPos = this.getMouseCoordinatesCanvas(e, this.canvas);
}
onMouseMove(e) {
this.currentPos = this.getMouseCoordinatesCanvas(e, this.canvas);
switch (this.tool) {
case TOOL_LINE:
this.drawShape();
break;
default:
break;
}
}
onMouseUp(e) {
this.canvas.onmousemove = null;
document.onmouseup = null;
}
drawShape() {
this.context.putImageData(this.saveData, 0, 0);
this.context.beginPath();
this.context.moveTo(this.startPos.x, this.startPos.y);
this.context.lineTo(this.currentPos.x, this.currentPos.y);
this.context.stroke();
}
getMouseCoordinatesCanvas(e, canvas) {
let rect = canvas.getBoundingClientRect();
let x = e.clientX - rect.left;
let y = e.clientY - rect.top;
return new Point(x, y);
}
}
var paint = new Paint("canvas");
paint.activeTool = TOOL_LINE;
paint.init();
document.querySelectorAll("[data-tools]").forEach(
item => {
item.addEventListener("click", e => {
let selectedTool = item.getAttribute("data-tools");
paint.activeTool = selectedTool;
});
}
);
#Container {
background-color: lime;
height: 310px;
}
.toolbox,
#canvas {
display: inline-block;
}
.toolbox {
background-color: gray;
padding: 0px 15px 15px 15px;
left: 10px;
top: 11px;
}
.group {
margin: 5px 2px;
}
#line {
transform: rotate(-90deg);
}
.ico {
margin: 3px;
font-size: 23px;
}
.item:hover,
.item.active {
background-color: rgba(160, 160, 160, 0.5);
color: white;
}
#canvas {
background-color: white;
margin: 5px;
float: right;
width: 400px;
height: 300px;
}
<script src="https://kit.fontawesome.com/c1d28c00bc.js" crossorigin="anonymous"></script>
<div class="container">
<div id="Container">
<div class="toolbox">
<center>
<div class="group tools">
<div class="item active" data-tools="line">
<i class="ico far fa-window-minimize" id="line" title="Line"></i>
</div>
</div>
</center>
</div>
<canvas id="canvas"></canvas>
</div>
</div>
Here is link of code.
Thanks in advance.
The issue is 1 or both of 2 things
Your canvas is being displayed at 400x300 but it only has 300x150 pixels. Canvases have 2 sizes. One size is the size they are displayed set with CSS. The other is how many pixels which is usually set in code by setting canvas.width and canvas.height. The default number of pixels is 300x150
If you actually want them to be different sizes then you need to take that into account in your mouse code. The correct code is
getMouseCoordinatesCanvas(e, canvas) {
let rect = canvas.getBoundingClientRect();
let x = (e.clientX - rect.left) * canvas.width / rect.width;
let y = (e.clientY - rect.top) * canvas.height / rect.height;
return new Point(x, y);
}
const TOOL_LINE = 'line';
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class Paint {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.context = canvas.getContext("2d");
}
set activeTool(tool) {
this.tool = tool;
}
init() {
this.canvas.onmousedown = e => this.onMouseDown(e);
}
onMouseDown(e) {
this.saveData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
this.canvas.onmousemove = e => this.onMouseMove(e);
document.onmouseup = e => this.onMouseUp(e);
this.startPos = this.getMouseCoordinatesCanvas(e, this.canvas);
}
onMouseMove(e) {
this.currentPos = this.getMouseCoordinatesCanvas(e, this.canvas);
switch (this.tool) {
case TOOL_LINE:
this.drawShape();
break;
default:
break;
}
}
onMouseUp(e) {
this.canvas.onmousemove = null;
document.onmouseup = null;
}
drawShape() {
this.context.putImageData(this.saveData, 0, 0);
this.context.beginPath();
this.context.moveTo(this.startPos.x, this.startPos.y);
this.context.lineTo(this.currentPos.x, this.currentPos.y);
this.context.stroke();
}
getMouseCoordinatesCanvas(e, canvas) {
let rect = canvas.getBoundingClientRect();
let x = (e.clientX - rect.left) * canvas.width / rect.width;
let y = (e.clientY - rect.top) * canvas.height / rect.height;
return new Point(x, y);
}
}
var paint = new Paint("canvas");
paint.activeTool = TOOL_LINE;
paint.init();
document.querySelectorAll("[data-tools]").forEach(
item => {
item.addEventListener("click", e => {
let selectedTool = item.getAttribute("data-tools");
paint.activeTool = selectedTool;
});
}
);
#Container {
background-color: lime;
height: 310px;
}
.toolbox,
#canvas {
display: inline-block;
}
.toolbox {
background-color: gray;
padding: 0px 15px 15px 15px;
left: 10px;
top: 11px;
}
.group {
margin: 5px 2px;
}
#line {
transform: rotate(-90deg);
}
.ico {
margin: 3px;
font-size: 23px;
}
.item:hover,
.item.active {
background-color: rgba(160, 160, 160, 0.5);
color: white;
}
#canvas {
background-color: white;
margin: 5px;
float: right;
width: 400px;
height: 300px;
}
<script src="https://kit.fontawesome.com/c1d28c00bc.js" crossorigin="anonymous"></script>
<div class="container">
<div id="Container">
<div class="toolbox">
<center>
<div class="group tools">
<div class="item active" data-tools="line">
<i class="ico far fa-window-minimize" id="line" title="Line"></i>
</div>
</div>
</center>
</div>
<canvas id="canvas"></canvas>
</div>
</div>
If you don't want them to be different sizes then you need to make the sizes match. I always set the size using CSS and then use code to make the canvas match that size.
Something like this
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
canvas.width = width;
canvas.height = height;
}
return needResize;
}
Note that anytime you change the canvas size it will get cleared but in any case.
const TOOL_LINE = 'line';
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
canvas.width = width;
canvas.height = height;
}
return needResize;
}
class Paint {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.context = canvas.getContext("2d");
resizeCanvasToDisplaySize(canvas);
}
set activeTool(tool) {
this.tool = tool;
}
init() {
this.canvas.onmousedown = e => this.onMouseDown(e);
}
onMouseDown(e) {
this.saveData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
this.canvas.onmousemove = e => this.onMouseMove(e);
document.onmouseup = e => this.onMouseUp(e);
this.startPos = this.getMouseCoordinatesCanvas(e, this.canvas);
}
onMouseMove(e) {
this.currentPos = this.getMouseCoordinatesCanvas(e, this.canvas);
switch (this.tool) {
case TOOL_LINE:
this.drawShape();
break;
default:
break;
}
}
onMouseUp(e) {
this.canvas.onmousemove = null;
document.onmouseup = null;
}
drawShape() {
this.context.putImageData(this.saveData, 0, 0);
this.context.beginPath();
this.context.moveTo(this.startPos.x, this.startPos.y);
this.context.lineTo(this.currentPos.x, this.currentPos.y);
this.context.stroke();
}
getMouseCoordinatesCanvas(e, canvas) {
let rect = canvas.getBoundingClientRect();
let x = (e.clientX - rect.left) * canvas.width / rect.width;
let y = (e.clientY - rect.top) * canvas.height / rect.height;
return new Point(x, y);
}
}
var paint = new Paint("canvas");
paint.activeTool = TOOL_LINE;
paint.init();
document.querySelectorAll("[data-tools]").forEach(
item => {
item.addEventListener("click", e => {
let selectedTool = item.getAttribute("data-tools");
paint.activeTool = selectedTool;
});
}
);
#Container {
background-color: lime;
height: 310px;
}
.toolbox,
#canvas {
display: inline-block;
}
.toolbox {
background-color: gray;
padding: 0px 15px 15px 15px;
left: 10px;
top: 11px;
}
.group {
margin: 5px 2px;
}
#line {
transform: rotate(-90deg);
}
.ico {
margin: 3px;
font-size: 23px;
}
.item:hover,
.item.active {
background-color: rgba(160, 160, 160, 0.5);
color: white;
}
#canvas {
background-color: white;
margin: 5px;
float: right;
width: 400px;
height: 300px;
}
<script src="https://kit.fontawesome.com/c1d28c00bc.js" crossorigin="anonymous"></script>
<div class="container">
<div id="Container">
<div class="toolbox">
<center>
<div class="group tools">
<div class="item active" data-tools="line">
<i class="ico far fa-window-minimize" id="line" title="Line"></i>
</div>
</div>
</center>
</div>
<canvas id="canvas"></canvas>
</div>
</div>
A common reason to make them different sizes is to support HI-DPI displays. In that case though the mouse code can go back to the way it was if you use the canvas transform.
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth * devicePixelRatio | 0;
const height = canvas.clientHeight * devicePixelRatio | 0;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
canvas.width = width;
canvas.height = height;
}
return needResize;
}
and then set the transform before drawing
ctx.scale(devicePixelRatio, devicePixelRatio);
const TOOL_LINE = 'line';
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth * devicePixelRatio | 0;
const height = canvas.clientHeight * devicePixelRatio | 0;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
canvas.width = width;
canvas.height = height;
}
return needResize;
}
class Paint {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.context = canvas.getContext("2d");
resizeCanvasToDisplaySize(canvas);
}
set activeTool(tool) {
this.tool = tool;
}
init() {
this.canvas.onmousedown = e => this.onMouseDown(e);
}
onMouseDown(e) {
this.saveData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
this.canvas.onmousemove = e => this.onMouseMove(e);
document.onmouseup = e => this.onMouseUp(e);
this.startPos = this.getMouseCoordinatesCanvas(e, this.canvas);
}
onMouseMove(e) {
this.currentPos = this.getMouseCoordinatesCanvas(e, this.canvas);
switch (this.tool) {
case TOOL_LINE:
this.drawShape();
break;
default:
break;
}
}
onMouseUp(e) {
this.canvas.onmousemove = null;
document.onmouseup = null;
}
drawShape() {
this.context.setTransform(1, 0, 0, 1, 0, 0); // the default
this.context.putImageData(this.saveData, 0, 0);
this.context.scale(devicePixelRatio, devicePixelRatio);
this.context.beginPath();
this.context.moveTo(this.startPos.x, this.startPos.y);
this.context.lineTo(this.currentPos.x, this.currentPos.y);
this.context.stroke();
}
getMouseCoordinatesCanvas(e, canvas) {
let rect = canvas.getBoundingClientRect();
let x = (e.clientX - rect.left);
let y = (e.clientY - rect.top);
return new Point(x, y);
}
}
var paint = new Paint("canvas");
paint.activeTool = TOOL_LINE;
paint.init();
document.querySelectorAll("[data-tools]").forEach(
item => {
item.addEventListener("click", e => {
let selectedTool = item.getAttribute("data-tools");
paint.activeTool = selectedTool;
});
}
);
#Container {
background-color: lime;
height: 310px;
}
.toolbox,
#canvas {
display: inline-block;
}
.toolbox {
background-color: gray;
padding: 0px 15px 15px 15px;
left: 10px;
top: 11px;
}
.group {
margin: 5px 2px;
}
#line {
transform: rotate(-90deg);
}
.ico {
margin: 3px;
font-size: 23px;
}
.item:hover,
.item.active {
background-color: rgba(160, 160, 160, 0.5);
color: white;
}
#canvas {
background-color: white;
margin: 5px;
float: right;
width: 400px;
height: 300px;
}
<script src="https://kit.fontawesome.com/c1d28c00bc.js" crossorigin="anonymous"></script>
<div class="container">
<div id="Container">
<div class="toolbox">
<center>
<div class="group tools">
<div class="item active" data-tools="line">
<i class="ico far fa-window-minimize" id="line" title="Line"></i>
</div>
</div>
</center>
</div>
<canvas id="canvas"></canvas>
</div>
</div>
note this line
this.saveData = this.context.getImageData(0, 0, this.canvas.clientWidth, this.canvas.clientHeight);
was wrong too. It should be
this.saveData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
clientWidth and clientHeight are the display size. width and height are the resolution (number of pixels in the canvas)

how to make a shadow in HTML canvas

I need to draw a canvas rect with shadow which has shadows on four sides of the rect, similar to a div has style as "box-shadow":"0px 0px 5px 5px"
You can use context.shadowColor plus context.shadowBlur to create the box-shadow effect.
Canvas's blur is very light so you must often overdraw the blurs to make them prominent.
Here's my version of your box-shadow: 0 0 5px 5px:
Example annotated code:
shadowRect(10,10,105,45,5,'red');
function shadowRect(x,y,w,h,repeats,color){
// set stroke & shadow to the same color
ctx.strokeStyle=color;
ctx.shadowColor=color;
// set initial blur of 3px
ctx.shadowBlur=3;
// repeatedly overdraw the blur to make it prominent
for(var i=0;i<repeats;i++){
// increase the size of blur
ctx.shadowBlur+=0.25;
// stroke the rect (which also draws its shadow)
ctx.strokeRect(x,y,w,h);
}
// cancel shadowing by making the shadowColor transparent
ctx.shadowColor='rgba(0,0,0,0)';
// restroke the interior of the rect for a more solid colored center
ctx.lineWidth=2;
ctx.strokeRect(x+2,y+2,w-4,h-4);
}
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
shadowRect(10,10,105,45,5,'red');
function shadowRect(x,y,w,h,repeats,color){
// set stroke & shadow to the same color
ctx.strokeStyle=color;
ctx.shadowColor=color;
// set initial blur of 3px
ctx.shadowBlur=3;
// repeatedly overdraw the blur to make it prominent
for(var i=0;i<repeats;i++){
// increase the size of blur
ctx.shadowBlur+=0.25;
// stroke the rect (which also draws its shadow)
ctx.strokeRect(x,y,w,h);
}
// cancel shadowing by making the shadowColor transparent
ctx.shadowColor='rgba(0,0,0,0)';
// restroke the interior of the rect for a more solid colored center
ctx.lineWidth=2;
ctx.strokeRect(x+2,y+2,w-4,h-4);
}
body{ background-color: ivory; padding:10px;}
<h4>box-shadow: 0 0 5px 5px red</h4>
<div style="box-shadow: 0 0 5px 5px red; height: 40px; width:100px;"></div>
<br>
<h4>Canvas "box-shadow" using context shadowing</h4>
<canvas id="canvas" width=200 height=100></canvas>
CanvasRenderingContext2D.shadowColor property in combination with shadowBlur, shadowOffsetX, or shadowOffsetY does just that.
Example:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const shadowRect = ({
x = 15,
y = 15,
w = 100,
h = 40,
fillStyle = 'tomato',
shadowOffsetX = 0,
shadowOffsetY = 3,
shadowBlur = 15,
shadowColor = "rgba(21, 24, 50, 0.3)"
} = {}) => {
ctx.shadowOffsetX = shadowOffsetX;
ctx.shadowOffsetY = shadowOffsetY;
ctx.shadowBlur = shadowBlur;
ctx.shadowColor = shadowColor;
ctx.fillStyle = fillStyle;
ctx.fillRect(x, y, w, h);
}
shadowRect({ fillStyle: '#C99DFE' });
body {
background-color: #E5E5E5;
padding: 10px;
font-family: sans-serif;
font-size: 16px;
}
.shadowRect {
margin: 15px 0 0 15px;
background-color: #C99DFE;
box-shadow: 0px 3px 15px rgba(21, 24, 50, 0.3);
border-radius: 2px;
width:100px;
height: 40px;
}
<h4>CSS</h4>
<pre>
width: 100px;
height: 40px;
margin: 15px 0 0 15px;
background-color: #C99DFE;
box-shadow: 0px 3px 15px rgba(21, 24, 50, 0.3);
</pre>
<div class="shadowRect"></div>
<br>
<h4>Canvas</h4>
<pre>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.shadowOffsetX = 15;
ctx.shadowOffsetY = 15;
ctx.shadowBlur = 15;
ctx.shadowColor = 'rgba(21, 24, 50, 0.3)';
ctx.fillStyle = '#C99DFE';
ctx.fillRect(15, 15, 100, 40);
</pre>
<canvas id="canvas" width=200 height=100></canvas>

Sudden change in picture quality in html canvas

I am making a canvas project and using some .png pictures. I used it in one file, canvas drew it up perfectly and all was well. But when i tried to to the same thing in a different file the quality of the pictures sank for no apparent reason. Would love some help/explenation to why this occured!
Here are the difference in coding, just open the files to see the difference in quality.
"Good quality code"
<!DOCTYPE html>
<html>
<head>
<title>Övning 1</title>
<style type="text/css">
*,body{
margin: 0px;
padding: 0px;
text-align: center;
background:url('bilder/background.png');
}
canvas{
border: 1px #999999 solid;
margin-top: 50px;
box-shadow: 2px 2px 1px rgba(50, 50, 50, 0.75) inset;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="900" height="400"></canvas>
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var path = "bilder/helnot.png";
var image1 = new DragImage(path, 200, 100);
var image2 = new DragImage(path, 300, 100);
var gKlav = new Image();
gKlav.src = "bilder/g-klav.png";
var loop = setInterval(function() {
ctx.fillStyle = "#FFFFFF";
ctx.fillRect(0, 0, 900, 400);
image1.update();
image2.update();
}, 30);
var mouseX = 0;
var mouseY = 0;
var mousePressed = false;
var dragging = false;
function DrawLines(){
ctx.fillStyle = "#000000";
ctx.fillRect(50,160,800,1);
ctx.fillRect(50,180,800,1);
ctx.fillRect(50,200,800,1);
ctx.fillRect(50,220,800,1);
ctx.fillRect(50,240,800,1);
ctx.drawImage(gKlav, 40, 40);
}
//Saves the cursers location into two vairables
$(document).mousemove(function(e) {
mouseX = e.offsetX;
mouseY = e.offsetY;
})
//Specifies what happens when the mouse is clicked
$(document).mousedown(function() {
mousePressed = true;
}).mouseup(function() {
mousePressed = false;
dragging = false;
});
//'this.x' and 'that.x' refers to the location of the top left corner of the image.
//'startX' and 'startY' refers to the distance between the curser and the image.
function DragImage(src, x, y) {
var that = this;
var startX = 0;
var startY = 0;
var drag = false;
this.x = x;
this.y = y;
var img = new Image();
img.src = src;
this.update = function() {
if (mousePressed) {
var left = that.x;
var right = that.x + img.width;
var top = that.y;
var bottom = that.y + img.height;
if (!drag) {
startX = mouseX - that.x;
startY = mouseY - that.y;
}
if (mouseX < right && mouseX > left && mouseY < bottom && mouseY > top) {
if (!dragging){
dragging = true;
drag = true;
}
}
} else {
drag = false;
$(document).mouseup(function(){
if((that.y + (img.height/2)) > 145 && (that.y + (img.height/2)) < 155){that.y = 140}
if((that.y + (img.height/2)) > 155 && (that.y + (img.height/2)) < 165){that.y = 150}
if((that.y + (img.height/2)) > 165 && (that.y + (img.height/2)) < 175){that.y = 160}
if((that.y + (img.height/2)) > 175 && (that.y + (img.height/2)) < 185){that.y = 170}
if((that.y + (img.height/2)) > 185 && (that.y + (img.height/2)) < 195){that.y = 180}
if((that.y + (img.height/2)) > 195 && (that.y + (img.height/2)) < 205){that.y = 190}
});
}
//Makes the image 'drag' across the screen.
if (drag) {
that.x = mouseX - startX;
that.y = mouseY - startY;
}
ctx.drawImage(img, that.x, that.y);
DrawLines();
}
}
</script>
</body>
</html>
"Bad quality code"
<!DOCTYPE html>
<html>
<head>
<title>Övning 2</title>
<style type="text/css">
*,body{
margin: 0px;
padding: 0px;
text-align: center;
background:url('bilder/background.png');
}
canvas{
border: 1px #999999 solid;
margin-top: 50px;
box-shadow: 2px 2px 1px rgba(50, 50, 50, 0.75) inset;
}
button{
white-space:nowrap;
display:inline-block;
position:relative;
background:#C22E3A;
border:1px black solid;
text-decoration:none;
color:white;
padding: 6px 9px;
border-radius:3px;
margin:5px;
padding-left:50px;
padding-right: 50px;
-webkit-box-shadow: 0 5px 1px rgba(0,0,0,0.2);
text-shadow: 0px -1px 0px rgba(0,0,0,0.5);
transition: background-color linear 0.07s;
}
button:active{
top:3px;
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.3);
}
#inputBox{
display: none;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="900" height="400"></canvas><br />
<button id="start" onclick="Activate()">Start</button><br />
<input type="text" id="inputBox">
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var start = false;
var helnot = "bilder/helnot.png";
var helnot2 = new RandomizeNote(helnot);
var gKlav = new Image();
gKlav.src = "bilder/g-klav.png";
var loop = setInterval(function() {
if(start){
document.getElementById('start').style.display = "none";
document.getElementById('inputBox').style.display = "inline";
helnot2.update();
}
Sheet();
}, 30);
function Sheet(){
ctx.fillStyle = "#000000";
ctx.fillRect(200,160,500,1);
ctx.fillRect(200,180,500,1);
ctx.fillRect(200,200,500,1);
ctx.fillRect(200,220,500,1);
ctx.fillRect(200,240,500,1);
ctx.drawImage(gKlav, 200, 150);
}
function Activate(){
start = true;
}
function RandomizeNote(src){
var img = new Image();
img.src = src;
this.update = function(){
ctx.drawImage(img, 430, 150);
}
}
</script>
</body>
</html>

Categories