I am trying to create an "Etch-A-Sketch"-program, which should let the user draw only by clicking or holding the mouse-button. How can I realize that in JavaScript?
Somehow after the user chooses a color via clicking on the color-button, the color is already drawn as soon as the mouse cursor enters the drawing area (div class="container").
I've tried several functions, but it's still not working as expected...
Could someone please provide a hint?
"use strict";
const divContainer = document.querySelector(".container");
const btnsContainer = document.querySelector(".buttons");
const btnBlack = document.createElement("button");
const btnGreyScale = document.createElement("button");
const btnRgb = document.createElement("button");
const btnErase = document.createElement("button");
const btnShake = document.createElement("button");
function createGrid(col, rows) {
for(let i = 0; i < (col * rows); i++) {
const div = document.createElement("div");
divContainer.style.gridTemplateColumns = `repeat(${col}, 1fr)`;
divContainer.style.gridTemplateRows = `repeat(${rows}, 1fr)`;
divContainer.appendChild(div).classList.add("box");
}
}
createGrid(16,16)
let isDrawing = false;
window.addEventListener("mousedown", () => {
isDrawing = true;
});
window.addEventListener("mouseup", () => {
isDrawing = false;
});
function paintBlack() {
const boxes = divContainer.querySelectorAll(".box");
btnBlack.textContent = "Black";
btnBlack.addEventListener("click", function () {
boxes.forEach(box => box.addEventListener("mouseover", function () {
this.style.background = "#000";
}))
})
btnsContainer.appendChild(btnBlack).classList.add("btn");
}
paintBlack();
function paintGreyScale() {
const boxes = divContainer.querySelectorAll(".box");
btnGreyScale.textContent = "Grey";
btnGreyScale.addEventListener("click", function () {
boxes.forEach(box => box.addEventListener("mouseover", function () {
let randNum = Math.floor(Math.random() * 256);
let grayScale = `rgb(${randNum},${randNum},${randNum})`;
box.style.background = grayScale;
}))
})
btnsContainer.appendChild(btnGreyScale).classList.add("btn");
}
paintGreyScale();
function paintRgb() {
const boxes = divContainer.querySelectorAll(".box");
btnRgb.textContent = "Rainbow";
btnRgb.addEventListener("click", function () {
boxes.forEach(box => box.addEventListener("mouseover", function () {
let r = Math.floor(Math.random() * 256);
let g = Math.floor(Math.random() * 256);
let b = Math.floor(Math.random() * 256);
const rgb = `rgb(${r},${g},${b})`;
box.style.background = rgb;
}))
})
btnsContainer.appendChild(btnRgb).classList.add("btn");
}
paintRgb();
function erase() {
const boxes = divContainer.querySelectorAll(".box");
btnErase.textContent = "Erase";
btnErase.addEventListener("click", function () {
boxes.forEach(box => box.addEventListener("mouseover", function () {
this.style.background = "#FFF";
}))
})
btnsContainer.appendChild(btnErase).classList.add("btn");
}
erase();
function clearCanvas() {
const boxes = divContainer.querySelectorAll(".box");
btnShake.textContent = "Shake it!";
btnShake.addEventListener("click", function () {
boxes.forEach(box => box.style.backgroundColor = "#FFF");
})
btnsContainer.appendChild(btnShake).classList.add("shake");
}
clearCanvas();
btnShake.addEventListener("click", clearCanvas);
*,
*::before,
*::after {
margin: 0;
padding: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
html {
font-size: 16px;
}
body {
background: linear-gradient(to bottom, #1488CC, #2B32B2);
color: #FFF;
line-height: 1.5;
height: 100vh;
}
#wrapper {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
text-align: center;
}
.container {
width: 500px;
height: 500px;
display: grid;
background-color: #FFF;
box-shadow: 0 0 10px;
}
.box {
border: .5px solid #808080;
}
.shake {
animation: shake .5s linear 1;
}
#keyframes shake {
10%,
90% {
transform: translate3d(-1px, 0, 0);
}
20%,
80% {
transform: translate3d(2px, 0, 0);
}
30%,
50%,
70% {
transform: translate3d(-4px, 0, 0);
}
40%,
60% {
transform: translate3d(4px, 0, 0);
}
}
<!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.0, shrink-to-fit=no">
<title>Etch-A-Sketch</title>
</head>
<body>
<div id="wrapper">
<main>
<div class="container"></div>
<div class="buttons"></div>
</main>
</div>
<script src="etchAsketch.js"></script>
</body>
</html>
The mousedown event
window.addEventListener("mousedown", () => {
isDrawing = true;
});
sets isDrawing to true, and mouseup event to false, but you never use this variable to check whether the color should be drawn.
Solution: for each statement you have that's setting the background color of a square (except for clearCanvas), wrap it in an if statement checking if the user isDrawing:
if (isDrawing){this.style.background = "#000";} //black
if (isDrawing){this.style.background = grayScale;} //gray
if (isDrawing){this.style.background = rgb;} // rainbow
if (isDrawing){this.style.background = "#FFF";} // erase
boxes.forEach(box => box.style.backgroundColor = "#FFF");
}) //leave clearCanvas as it is
Related
const canvasEle = document.getElementById('drawing-container');
const canvasPad = document.getElementById('pad');
const toolbar = document.getElementById('toolbar');
const context = canvasEle.getContext('2d');
const padContext = canvasPad.getContext('2d');
const canvasOffsetX = canvas.offsetLeft;
const canvasOffsetY = canvas.offsetTop;
canvas.width = window.innerWidth - canvasOffsetX;
canvas.height = window.innerHeight - canvasOffsetY;
let startPosition = {
x: 0,
y: 0
};
let lineCoordinates = {
x: 0,
y: 0
};
let isDrawStart = false;
const getClientOffset = (event) => {
const {
pageX,
pageY
} = event.touches ? event.touches[0] : event;
const x = pageX - canvasPad.offsetLeft;
const y = pageY - canvasPad.offsetTop;
return {
x,
y
}
}
toolbar.addEventListener('click', e => {
if (e.target.id === 'clear') {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
});
toolbar.addEventListener('change', e => {
if(e.target.id === 'stroke') {
ctx.strokeStyle = e.target.value;
}
if(e.target.id === 'lineWidth') {
lineWidth = e.target.value;
}
});
const drawLine = (ctx) => {
if (!isDrawStart) {
return;
}
ctx.beginPath();
ctx.moveTo(startPosition.x, startPosition.y);
ctx.lineTo(lineCoordinates.x, lineCoordinates.y);
ctx.stroke();
}
const mouseDownListener = (event) => {
startPosition = getClientOffset(event);
isDrawStart = true;
}
const mouseMoveListener = (event) => {
if (!isDrawStart) return;
lineCoordinates = getClientOffset(event);
clearCanvas(padContext);
drawLine(padContext);
}
const mouseupListener = (event) => {
clearCanvas(padContext);
drawLine(context);
isDrawStart = false;
}
const clearCanvas = (ctx) => {
ctx.clearRect(0, 0, canvasEle.width, canvasEle.height);
}
canvasPad.addEventListener('mousedown', mouseDownListener);
canvasPad.addEventListener('mousemove', mouseMoveListener);
canvasPad.addEventListener('mouseup', mouseupListener);
body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
color: white;
}
h1 {
background: #7F7FD5;
background: -webkit-linear-gradient(to right, #91EAE4, #86A8E7, #7F7FD5);
background: linear-gradient(to right, #91EAE4, #86A8E7, #7F7FD5);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.cnv-wrapper {
height: 100%;
display: flex;
}
.pad {
height: 100%;
display: flex;
border: 4px solid #333;
}
.container {
height: 100%;
display: flex;
}
#toolbar {
display: flex;
flex-direction: column;
padding: 5px;
width: 70px;
background-color: #202020;
}
#toolbar * {
margin-bottom: 6px;
}
#toolbar label {
font-size: 12px;
}
#toolbar input {
width: 100%;
}
#toolbar button {
background-color: #1565c0;
border: none;
border-radius: 4px;
color:white;
padding: 2px;
}
<!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 = 0.1">
<link rel = "stylesheet" href = "styles.css">
<title> Pipe Illustrator </title>
</head>
<body>
<section class = "container">
<div id = "toolbar">
<h1>Draw.</h1>
<label for="stroke">Stroke</label>
<input id="stroke" name='stroke' type="color">
<label for="lineWidth">Line Width</label>
<input id="lineWidth" name='lineWidth' type="number" value="5">
<button id="clear">Clear</button>
</div>
<div class = "drawing-container">
<canvas id = "drawing-container"></canvas>
</div>
<div class = "cnv-wrapper">
<canvas id = "cnv-wrapper"></canvas>
</div>
</section>
<script src = "./index.js"></script>
</body>
</html>
The project is to make a simple line drawer which makes allows me to draw fixed straight lines without the previous line being removed. The code written below is exactly what I need and was provided by another user from a question I asked previously about making it as my code wouldn't allow for the previous line to remain when drawing a new one.
This is the code below which is in jsfiddle, when I copy and paste it into my Visual studio code, I don't get the same output as I would in jsfiddle, the canvas doesn't appear nor does it allow anything to be drawn. Am I missing something ?
Right so this is the code i have written in VSC. It isn't entirely what was in the jsfiddle, i tried to integrate some other functions from another paint project i did. I was trying to add a toolbar for line colour and width. I think the way i have written it is contradicting it all got quite messy and was quite a fail. These are projects im doing to learn code as i am still a beginner to coding. Thank you for your help and input.
const canvasEle = document.querySelector('.draw-container');
const canvasPad = document.querySelector('.pad');
const context = canvasEle.getContext('2d');
const padContext = canvasPad.getContext('2d');
let startPosition = {
x: 0,
y: 0
};
let lineCoordinates = {
x: 0,
y: 0
};
let isDrawStart = false;
const getClientOffset = (event) => {
const {
pageX,
pageY
} = event.touches ? event.touches[0] : event;
const x = pageX - canvasPad.offsetLeft;
const y = pageY - canvasPad.offsetTop;
return {
x,
y
}
}
const drawLine = (ctx) => {
if (!isDrawStart) {
return;
}
ctx.beginPath();
ctx.moveTo(startPosition.x, startPosition.y);
ctx.lineTo(lineCoordinates.x, lineCoordinates.y);
ctx.stroke();
}
const mouseDownListener = (event) => {
startPosition = getClientOffset(event);
isDrawStart = true;
}
const mouseMoveListener = (event) => {
if (!isDrawStart) return;
lineCoordinates = getClientOffset(event);
clearCanvas(padContext);
drawLine(padContext);
}
const mouseupListener = (event) => {
clearCanvas(padContext);
drawLine(context);
isDrawStart = false;
}
const clearCanvas = (ctx) => {
ctx.clearRect(0, 0, canvasEle.width, canvasEle.height);
}
canvasPad.addEventListener('mousedown', mouseDownListener);
canvasPad.addEventListener('mousemove', mouseMoveListener);
canvasPad.addEventListener('mouseup', mouseupListener);
body {
background-color: #ccc;
}
.cnv-wrapper {
position: relative;
}
.pad {
position: absolute;
top: 0px;
left: 0px;
border: 1px solid #333;
}
.draw-container {
border: 1px solid #333;
}
<div class="cnv-wrapper">
<canvas class="draw-container" width="500" height="500"></canvas>
<canvas class="pad" width="500" height="500"></canvas>
</div>
From comments where I advise:
You can't just copy and paste code [from fiddle] without also using the appropriate html tags. Your CSS needs to go in style tags. Your scripts need to go in script tags. The dom elements, e.g. the "canvas" needs to go in the body tag. Etc. ... You may also need to affix the appropriate "doctype" and you may need to trigger the scripts to run on document load.
As an example, I tried the pasted code using the above advice on my own computer and it worked.
I'll show the complete source and then followup with a runnable version here on SO. Note that the runnable version uses SO's utility for such and as such you won't see all the various tags I mentioned, but the functionality remains the same just as with the "fiddle".
Corrected "pasted" source showing all the relevant tags you need to create:
<!doctype html> <!-- Add appropriate doctype -->
<html>
<head>
<style>
/* Paste the CSS in a style tag */
body {
background-color: #ccc;
}
.cnv-wrapper {
position: relative;
}
.pad {
position: absolute;
top: 0px;
left: 0px;
border: 1px solid #333;
}
.draw-container {
border: 1px solid #333;
}
</style>
</head>
<body>
<div class="cnv-wrapper">
<canvas class="draw-container" width="500" height="500"></canvas>
<canvas class="pad" width="500" height="500"></canvas>
</div>
<script>
// Paste your init scripts into the onload event to ensure they get ran after the canvas is created
window.onload = function() {
const canvasEle = document.querySelector('.draw-container');
const canvasPad = document.querySelector('.pad');
const context = canvasEle.getContext('2d');
const padContext = canvasPad.getContext('2d');
let startPosition = {
x: 0,
y: 0
};
let lineCoordinates = {
x: 0,
y: 0
};
let isDrawStart = false;
const getClientOffset = (event) => {
const {
pageX,
pageY
} = event.touches ? event.touches[0] : event;
const x = pageX - canvasPad.offsetLeft;
const y = pageY - canvasPad.offsetTop;
return {
x,
y
}
}
const drawLine = (ctx) => {
if (!isDrawStart) {
return;
}
ctx.beginPath();
ctx.moveTo(startPosition.x, startPosition.y);
ctx.lineTo(lineCoordinates.x, lineCoordinates.y);
ctx.stroke();
}
const mouseDownListener = (event) => {
startPosition = getClientOffset(event);
isDrawStart = true;
}
const mouseMoveListener = (event) => {
if (!isDrawStart) return;
lineCoordinates = getClientOffset(event);
clearCanvas(padContext);
drawLine(padContext);
}
const mouseupListener = (event) => {
clearCanvas(padContext);
drawLine(context);
isDrawStart = false;
}
const clearCanvas = (ctx) => {
ctx.clearRect(0, 0, canvasEle.width, canvasEle.height);
}
canvasPad.addEventListener('mousedown', mouseDownListener);
canvasPad.addEventListener('mousemove', mouseMoveListener);
canvasPad.addEventListener('mouseup', mouseupListener);
}
</script>
</body>
</html>
Now on to demonstrating this code works now here on SO.
Note that SO's intepreter of code also (like "fiddle") doesn't need the tags mentioned that would be needed when you copy and paste into your own editor. Therefore I re-mention where the pasted code goes in code comments.
// Your scripts will go in a <script> tag
// Use an onload event handler to ensure they get ran after the canvas is created
window.onload = function() {
const canvasEle = document.querySelector('.draw-container');
const canvasPad = document.querySelector('.pad');
const context = canvasEle.getContext('2d');
const padContext = canvasPad.getContext('2d');
let startPosition = {
x: 0,
y: 0
};
let lineCoordinates = {
x: 0,
y: 0
};
let isDrawStart = false;
const getClientOffset = (event) => {
const {
pageX,
pageY
} = event.touches ? event.touches[0] : event;
const x = pageX - canvasPad.offsetLeft;
const y = pageY - canvasPad.offsetTop;
return {
x,
y
}
}
const drawLine = (ctx) => {
if (!isDrawStart) {
return;
}
ctx.beginPath();
ctx.moveTo(startPosition.x, startPosition.y);
ctx.lineTo(lineCoordinates.x, lineCoordinates.y);
ctx.stroke();
}
const mouseDownListener = (event) => {
startPosition = getClientOffset(event);
isDrawStart = true;
}
const mouseMoveListener = (event) => {
if (!isDrawStart) return;
lineCoordinates = getClientOffset(event);
clearCanvas(padContext);
drawLine(padContext);
}
const mouseupListener = (event) => {
clearCanvas(padContext);
drawLine(context);
isDrawStart = false;
}
const clearCanvas = (ctx) => {
ctx.clearRect(0, 0, canvasEle.width, canvasEle.height);
}
canvasPad.addEventListener('mousedown', mouseDownListener);
canvasPad.addEventListener('mousemove', mouseMoveListener);
canvasPad.addEventListener('mouseup', mouseupListener);
}
/* Your CSS would go in a <style> tag */
body {
background-color: #ccc;
}
.cnv-wrapper {
position: relative;
}
.pad {
position: absolute;
top: 0px;
left: 0px;
border: 1px solid #333;
}
.draw-container {
border: 1px solid #333;
}
<!-- Your HTML would go in the <body> tag -->
<div class="cnv-wrapper">
<canvas class="draw-container" width="500" height="500"></canvas>
<canvas class="pad" width="500" height="500"></canvas>
</div>
I have a small page. Circles appear here, when you click on them they disappear.
I added here the possibility that in addition to clicking on the LMB, you can also click on a keyboard key, in my case it is "A".
But I noticed the problem that if you hold down the "A" button, then drive in circles, then the need for clicks disappears, and this is the main goal.
Can I somehow disable the ability to hold this button so that only clicking on it works?
I tried pasting this code, but all animations stopped working for me.
var down = false;
document.addEventListener('keydown', function () {
if(down) return;
down = true;
// your magic code here
}, false);
document.addEventListener('keyup', function () {
down = false;
}, false);
Get error:
"message": "Uncaught ReferenceError: mouseOverHandler is not defined"
//create circle
var clickEl = document.getElementById("clicks");
var spawnRadius = document.getElementById("spawnRadius");
var spawnArea = spawnRadius.getBoundingClientRect();
const circleSize = 95; // Including borders
function createDiv(id, color) {
let div = document.createElement('div');
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#ebc6df', '#ebc6c9', '#e1c6eb', '#c6c9eb', '#c6e8eb', '#e373fb', '#f787e6', '#cb87f7', '#87a9f7', '#87f7ee'];
randomColor = colors[Math.floor(Math.random() * colors.length)];
div.style.borderColor = randomColor;
}
else {
div.style.borderColor = color;
}
// Randomly position circle within spawn area
div.style.top = `${Math.floor(Math.random() * (spawnArea.height - circleSize))}px`;
div.style.left = `${Math.floor(Math.random() * (spawnArea.width - circleSize))}px`;
div.classList.add("circle", "animation");
// Add click handler
let clicked = false;
div.addEventListener('mouseover', mouseOverHandler );
div.addEventListener('mouseout', mouseOutHandler );
div.addEventListener('click', (event) => {
if (clicked) { return; } // Only allow one click per circle
clicked = true;
div.style.animation = 'Animation 200ms linear forwards';
setTimeout(() => { spawnRadius.removeChild(div); }, 220);
});
spawnRadius.appendChild(div);
}
let i = 0;
const rate = 1000;
setInterval(() => {
i += 1;
createDiv(`circle${i}`);
}, rate);
let focusedEl = null;
const keyDownHandler = (evt) => {
if(evt.keyCode === 65 && focusedEl) focusedEl.click();
}
const mouseOutHandler = (evt) => focusedEl = null;
const mouseOverHandler = (evt) => focusedEl = evt.currentTarget;
document.addEventListener('keydown', keyDownHandler );
window.focus();
html, body {
width: 100%;
height: 100%;
margin: 0;
background: #0f0f0f;
}
.circle {
width: 80px;
height: 80px;
border-radius: 80px;
background-color: #0f0f0f;
border: 3px solid #000;
position: absolute;
}
#spawnRadius {
top: 55%;
height: 250px;
width: 500px;
left: 50%;
white-space: nowrap;
position: absolute;
transform: translate(-50%, -50%);
background: #0f0f0f;
border: 2px solid #ebc6df;
}
#keyframes Animation {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(.8);
}
100% {
transform: scale(1);
opacity: 0;
}
}
<html>
<body>
<div id="spawnRadius"></div>
</body>
</html>
You can check the repeat property of the event, which
… is true if the given key is being held down such that it is automatically repeating.
(but notice the compatibility notes on auto-repeat handling)
const keyDownHandler = (evt) => {
if (!evt.repeat && evt.keyCode === 65) {
// ^^^^^^^^^^^
focusedEl?.click();
}
}
//create circle
var clickEl = document.getElementById("clicks");
var spawnRadius = document.getElementById("spawnRadius");
var spawnArea = spawnRadius.getBoundingClientRect();
const circleSize = 95; // Including borders
function createDiv(id, color) {
let div = document.createElement('div');
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#ebc6df', '#ebc6c9', '#e1c6eb', '#c6c9eb', '#c6e8eb', '#e373fb', '#f787e6', '#cb87f7', '#87a9f7', '#87f7ee'];
randomColor = colors[Math.floor(Math.random() * colors.length)];
div.style.borderColor = randomColor;
}
else {
div.style.borderColor = color;
}
// Randomly position circle within spawn area
div.style.top = `${Math.floor(Math.random() * (spawnArea.height - circleSize))}px`;
div.style.left = `${Math.floor(Math.random() * (spawnArea.width - circleSize))}px`;
div.classList.add("circle", "animation");
// Add click handler
let clicked = false;
div.addEventListener('mouseover', mouseOverHandler );
div.addEventListener('mouseout', mouseOutHandler );
div.addEventListener('click', (event) => {
if (clicked) { return; } // Only allow one click per circle
clicked = true;
div.style.animation = 'Animation 200ms linear forwards';
setTimeout(() => { spawnRadius.removeChild(div); }, 220);
});
spawnRadius.appendChild(div);
}
let i = 0;
const rate = 1000;
setInterval(() => {
i += 1;
createDiv(`circle${i}`);
}, rate);
let focusedEl = null;
const mouseOutHandler = (evt) => focusedEl = null;
const mouseOverHandler = (evt) => focusedEl = evt.currentTarget;
document.addEventListener('keydown', keyDownHandler );
window.focus();
html, body {
width: 100%;
height: 100%;
margin: 0;
background: #0f0f0f;
}
.circle {
width: 80px;
height: 80px;
border-radius: 80px;
background-color: #0f0f0f;
border: 3px solid #000;
position: absolute;
}
#spawnRadius {
top: 55%;
height: 250px;
width: 500px;
left: 50%;
white-space: nowrap;
position: absolute;
transform: translate(-50%, -50%);
background: #0f0f0f;
border: 2px solid #ebc6df;
}
#keyframes Animation {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(.8);
}
100% {
transform: scale(1);
opacity: 0;
}
}
<html>
<body>
<div id="spawnRadius"></div>
</body>
</html>
When starting my program, everything initially runs as intended. However, when I click a button on the webpage that runs my clearBoard() function, the code in the global scope no longer runs.
I've used console.log and everything that is outside of a function is empty after running the clearBoard().
const container = document.getElementById("container");
//Create board
function makeRows(rows, columns) {
//let container = document.getElementById("container");
container.style.setProperty('--grid-rows', rows);
container.style.setProperty('--grid-cols', columns);
for (c = 0; c < (rows * columns); c++) {
let cell = document.createElement("div");
//cell.innerText = (c + 1);
container.appendChild(cell).className = "grid-item";
}
}
//Draw starting Board
makeRows(16, 16);
//Paint on board
const paint = document.getElementsByClassName("grid-item");
console.log(container);
for (let i = 0; i < paint.length; i++) {
paint[i].addEventListener('mouseover', function() {
this.style.backgroundColor = "black";
});
}
//Get new board size
function getNewBoard() {
//clear divs
const removeOldGrid = document.getElementById("container");
removeOldGrid.innerHTML = '';
//get input from user
let squares = prompt('How many squares per side?');
//convert input to number
let squaresInt = parseInt(squares);
//generate new board
makeRows(squaresInt, squaresInt);
}
//Clear board
function clearBoard() {
for (let i = 0; i < paint.length; i++) {
paint[i].style.backgroundColor = "";
}
getNewBoard();
}
:root {
--grid-cols: 0;
--grid-rows: 0;
--paint-color: ;
}
#container {
display: grid;
padding: 10em;
height: 40vh;
width: 50vh;
margin-left: auto;
margin-right: auto;
/*grid-gap: 1em;*/
grid-template-rows: repeat(var(--grid-rows), 1fr);
grid-template-columns: repeat(var(--grid-cols), 1fr);
}
.grid-item {
padding: 1em;
border: 1px solid #ddd;
text-align: center;
/*background-color: var(--paint-color);*/
}
/*.grid-item:hover {
background-color: black;
}
<button onclick="clearBoard()">Clear Board</button>
<div id="container">
</div>
That is happening because you are creating a new board, consequently a new grid-item element. The mouseover event listener is added to the paint variable, which contains the old element after clearBoard() is called.
The solution is to add a new event listener to paint when a new board is generated. Your JS should look something like this:
//Paint on board
var paint = document.getElementsByClassName("grid-item");
console.log(container);
// Function to add the mouseover event listener when "paint" changes
function addMouseoverEventListener() {
for (let i = 0; i < paint.length; i++)
{
paint[i].addEventListener('mouseover', function() {
this.style.backgroundColor = "black";
});
}
}
addMouseoverEventListener();
//Get new board size
function getNewBoard() {
//clear divs
const removeOldGrid = document.getElementById("container");
removeOldGrid.innerHTML = '';
//get input from user
let squares = prompt('How many squares per side?');
//convert input to number
let squaresInt = parseInt(squares);
//generate new board
makeRows(squaresInt, squaresInt);
// Get the new element
paint = document.getElementsByClassName("grid-item");
// Add the event listener again
addMouseoverEventListener();
}
Instead of using paint[i].addEventListener use window.onmouseover and check the target
const container = document.getElementById("container");
//Create board
function makeRows (rows, columns) {
//let container = document.getElementById("container");
container.style.setProperty('--grid-rows', rows);
container.style.setProperty('--grid-cols', columns);
for (c = 0; c < (rows * columns); c++) {
let cell = document.createElement("div");
//cell.innerText = (c + 1);
container.appendChild(cell).className = "grid-item";
}
}
const paint = document.getElementsByClassName("grid-item");
//Draw starting Board
makeRows(16, 16);
window.onmouseover=function(e) {
if(e.target.className == "grid-item")
{
e.target.style.backgroundColor = "black";
}
};
//Get new board size
function getNewBoard() {
//clear divs
const removeOldGrid = document.getElementById("container");
removeOldGrid.innerHTML = '';
//get input from user
let squares = prompt('How many squares per side?');
//convert input to number
let squaresInt = parseInt(squares);
//generate new board
makeRows(squaresInt, squaresInt);
}
function clearBoard() {
for (let i = 0; i < paint.length; i++) {
paint[i].style.backgroundColor = "";
}
getNewBoard();
}
:root {
--grid-cols: 0;
--grid-rows: 0;
--paint-color: ;
}
#container {
display: grid;
padding: 10em;
height: 40vh;
width: 50vh;
margin-left: auto;
margin-right: auto;
/*grid-gap: 1em;*/
grid-template-rows: repeat(var(--grid-rows), 1fr);
grid-template-columns: repeat(var(--grid-cols), 1fr);
}
.grid-item {
padding: 1em;
border: 1px solid #ddd;
text-align: center;
/*background-color: var(--paint-color);*/
}
/*.grid-item:hover {
background-color: black;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Etch-a-Sketch</title>
<meta name="author" content="">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="style.css" rel="stylesheet">
</head>
<body>
<button onclick = "clearBoard()">Clear Board</button>
<div id="container">
</div>>
<script src="script.js"></script>
</body>
</html>
The dragging works but the bug is that when I drag the element somehow "jumps" and does not flow with the mouse. See it in the code. Don't worry about removing event listeners, I will add them as soon as this works.
I have an issue on a draggable "div" element. I've searched many answers before I posted this question but nothing seems to be the solution(or maybe I am not understanding the problem really well).
Thank you!
const hotspot = document.getElementsByClassName("hotspot")[0];
const container = document.getElementsByClassName("container")[0];
let containerRect = container.getBoundingClientRect();
let hsRect = hotspot.getBoundingClientRect();
let relMouse = { x: 0, y: 0 };
let windowMouse = { x: 0, y: 0 };
let isUserIntercating = false;
const handlePointerUp = (e) => {
isUserIntercating = false;
};
const handlePointerDown = (e) => {
isUserIntercating = true;
hsRect = hotspot.getBoundingClientRect();
relMouse = { x: e.pageX - hsRect.x, y: e.pageY - hsRect.y };
window.addEventListener("pointerup", handlePointerUp, false);
};
const handlePointerMove = (e) => {
hsRect = hotspot.getBoundingClientRect();
containerRect = container.getBoundingClientRect();
windowMouse = { x: e.clientX - relMouse.x, y: e.clientY - relMouse.y };
};
const update = (t) => {
requestAnimationFrame(update);
if (isUserIntercating) {
hotspot.style.transform = `translate(${
windowMouse.x - containerRect.x
}px,0px)`;
}
};
update();
hotspot.addEventListener("pointerdown", handlePointerDown, false);
window.addEventListener("pointermove", handlePointerMove, false);
body {
font-family: sans-serif;
margin: 0;
}
.container {
padding: 0;
margin: 50px;
max-width: 600px;
min-height: 600px;
background-color: blanchedalmond;
}
.hotspot {
/* position: absolute; */
background-color: aqua;
/* transform: translate(100px, 100px); */
min-height: 100px;
max-width: 100px;
z-index: 200;
}
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div class="container">
<div class="hotspot"></div>
</div>
<script src="src/index.js"></script>
</body>
</html>
Try like this:
const hotspot = document.getElementsByClassName("hotspot")[0];
const container = document.getElementsByClassName("container")[0];
let containerRect = container.getBoundingClientRect();
let hsRect = hotspot.getBoundingClientRect();
let relMouse = { x: 0, y: 0 };
let windowMouse = { x: 0, y: 0 };
let isUserIntercating = false;
const handlePointerUp = (e) => {
isUserIntercating = false;
};
const handlePointerDown = (e) => {
isUserIntercating = true;
hsRect = hotspot.getBoundingClientRect();
relMouse = { x: e.pageX - hsRect.x, y: e.pageY - hsRect.y };
window.addEventListener("pointerup", handlePointerUp, false);
};
const handlePointerMove = (e) => {
hsRect = hotspot.getBoundingClientRect();
containerRect = container.getBoundingClientRect();
windowMouse = { x: e.clientX - relMouse.x, y: e.clientY - relMouse.y };
requestAnimationFrame(update);
};
const update = (t) => {
if (isUserIntercating) {
hotspot.style.transform = `translate(${
windowMouse.x - containerRect.x
}px,0px)`;
}
};
hotspot.addEventListener("pointerdown", handlePointerDown, false);
window.addEventListener("pointermove", handlePointerMove, false);
body {
font-family: sans-serif;
margin: 0;
}
.container {
padding: 0;
margin: 50px;
max-width: 600px;
min-height: 600px;
background-color: blanchedalmond;
}
.hotspot {
/* position: absolute; */
background-color: aqua;
/* transform: translate(100px, 100px); */
min-height: 100px;
max-width: 100px;
z-index: 200;
}
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div class="container">
<div class="hotspot"></div>
</div>
<script src="src/index.js"></script>
</body>
</html>
The issue seemed to be that the update function kept calling itself all the time, which is not really ideal. The update should only be called in the handlePointerMove function (only change the hotspot position when the mouse moves).
I wanted to make an effect similar to "click and hold" of this page, but with some changes, with svg forms, the point is that I did two functions that do what I wanted to do very well, but at the moment I introduced another svg form, the Data of the effect is transferred to the other, affecting the execution of the functions, the question is, how do I prevent this from happening?
Note: The best way to see what is happening is to let one of the two complete.
Here is an example of what I have programmed
of course I leave you all the code that I have been working
//Efect Drivers
class EffectValues {
constructor(count, time, initOffset, label) {
this.count = count;
this.time = time;
this.initOffset = initOffset;
this.label = label;
}
}
//Controlers
let counter; //it will be interval controller
let globalCount = 0;
//Call objects DOM
const loader = document.querySelector('.loader');
const circle = document.querySelector('.circle');
const svgText = document.querySelector('.svgText');
const textSvg = document.querySelector('.textSvg');
//Preloader svg
const startCircle = new EffectValues(0, 3, 1300, circle);
const showEffect = new EffectValues(0, 3, 500, svgText);
//Mouse events
// Circle
loader.addEventListener('mousedown', e => {
increase(e, startCircle);
});
loader.addEventListener('mouseup', e => {
decrease(e, startCircle);
});
// Text SVG
textSvg.addEventListener('mousedown', e => {
increase(e, showEffect);
});
textSvg.addEventListener('mouseup', e => {
decrease(e, showEffect);
});
//main functions
const increase = (e, { count, initOffset, time, label }) => {
let flag = true;
// console.log(flag);
clearInterval(counter);
while (e.type == 'mousedown') {
counter = setInterval(() => {
if (globalCount < initOffset) {
count = initOffset - globalCount;
label.style.strokeDashoffset = count;
globalCount++;
}
}, time);
break;
}
return flag;
};
const decrease = (e, { count, initOffset, time, label }) => {
let flag = true;
// console.log(flag);
clearInterval(counter);
while (e.type == 'mouseup') {
counter = setInterval(() => {
if (globalCount >= 0 && globalCount < initOffset) {
count = -globalCount + initOffset;
label.style.strokeDashoffset = count;
globalCount--;
} else {
flag = false;
}
}, time);
break;
}
return flag;
};
:root {
--dark: #2f3640;
--dark-light: #353b48;
--blue: #192a56;
--blue-dark: #273c75;
--cian: #0097e6;
--cian-light: #00a8ff;
--orange: #c23616;
--orange-light: #e84118;
}
* {
margin: 0;
padding: 0;
}
body {
min-height: 100vh;
background-color: var(--dark);
display: flex;
justify-content: center;
align-content: center;
}
.loader {
position: relative;
width: 50%;
height: 100vh;
}
.loader svg {
position: absolute;
width: 550px;
height: 550px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.loader svg circle {
width: 100%;
height: 100%;
fill: none;
stroke-width: 10;
stroke: var(--cian);
stroke-linecap: round;
transform: translate(5px, 5px);
stroke-dasharray: 1300;
stroke-dashoffset: 1300;
}
.textSvg {
position: relative;
width: 40%;
}
.textSvg svg text {
stroke: var(--orange-light);
fill: none;
stroke-width: 3;
stroke-dasharray: 500;
stroke-dashoffset: 500;
}
<!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="styles.css" />
<title>Loader</title>
</head>
<body>
<div class="loader">
<svg>
<circle class="circle" cx="200" cy="200" r="200"></circle>
</svg>
</div>
<div class="textSvg">
<svg
xmlns="http://www.w3.org/2000/svg"
width="1413"
height="274"
viewBox="0 0 1413 274"
>
<text
class="svgText"
transform="translate(0 198)"
fill="#c6e0ee"
font-size="100"
font-family="MonotypeCorsiva, Monotype Corsiva"
>
<tspan x="0" y="0">David Figueroa</tspan>
</text>
</svg>
</div>
</body>
<script src="main.js" defer></script>
</html>
I have been looking for information but nothing has helped me.
Beforehand thank you very much
You can implement your requirements by moving global variables and functions inside the class.
Codepen here
//Efect Drivers
class EffectValues {
constructor(time, initOffset, label) {
this.time = time;
this.initOffset = initOffset;
this.label = label;
this.counter;
this.globalCount = 0;
}
increase(e) {
let flag = true;
// console.log(flag);
clearInterval(this.counter);
while (e.type == 'mousedown') {
this.counter = setInterval(() => {
if (this.globalCount < this.initOffset) {
const count = this.initOffset - this.globalCount;
this.label.style.strokeDashoffset = count;
this.globalCount++;
}
}, this.time);
break;
}
return flag;
};
decrease(e) {
let flag = true;
// console.log(flag);
clearInterval(this.counter);
while (e.type == 'mouseup') {
this.counter = setInterval(() => {
if (this.globalCount >= 0 && this.globalCount < this.initOffset) {
const count = -this.globalCount + this.initOffset;
this.label.style.strokeDashoffset = count;
this.globalCount--;
} else {
flag = false;
}
}, this.time);
break;
}
return flag;
};
}
//Call objects DOM
const loader = document.querySelector('.loader');
const circle = document.querySelector('.circle');
const svgText = document.querySelector('.svgText');
const textSvg = document.querySelector('.textSvg');
//Preloader svg
const startCircle = new EffectValues(3, 1300, circle);
const letterEffect = new EffectValues(3, 500, svgText);
//Mouse events
// Circle
loader.addEventListener('mousedown', e => {
startCircle.increase(e);
});
loader.addEventListener('mouseup', e => {
startCircle.decrease(e);
});
// Text SVG
textSvg.addEventListener('mousedown', e => {
letterEffect.increase(e);
});
textSvg.addEventListener('mouseup', e => {
letterEffect.decrease(e);
});