Некорректное отображение фигур
Я пока только изучаю JavaScript и написал программу, которая должна выводить фигуру на экран и при клике на нее выводить время, через которое на нее нажали, и так раз за разом.
После клика на первую фигуру она исчезает, но потом появляется вторая поверх первой и с каждым нажатием они всё больше накладываются друг на друга, хотя, по задумке, та фигура, на которую нажали, должна совсем исчезнуть. Что необходимо сделать?
var canv = document.getElementById('canvas');
var ctx = canv.getContext('2d');
var startTime = new Date().getTime();
///////////////////* Figures *///////////////////
var x = Math.floor(Math.random() * 70) + 45,
y = Math.floor(Math.random() * 50),
w = Math.floor(Math.random() * 200) + 75,
h = Math.floor(Math.random() * 200) + 75,
radius = Math.floor(Math.random() * 75),
radiusX = Math.floor(Math.random() * 60) + 20,
radiusY = Math.floor(Math.random() * 60) + 20;
function getRandomColor() {
var letters = "0123456789ABCDEF".split('');
var color = "#";
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
function rectangle() {
ctx.fillRect(x, y, w, h);
ctx.fillStyle = getRandomColor();
};
function circle() {
ctx.fillStyle = getRandomColor();
ctx.beginPath();
ctx.arc(75, 75, radius, 0, 2 * Math.PI);
ctx.stroke();
ctx.fill();
};
function ellipse() {
ctx.fillStyle = getRandomColor();
ctx.beginPath();
ctx.ellipse(100, 100, radiusX, radiusY, 50, 0, 300, false);
ctx.stroke();
ctx.fill();
};
function triangle() {
ctx.fillStyle = getRandomColor();
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(25, 100);
ctx.lineTo(75, 100);
ctx.closePath();
ctx.stroke();
ctx.fill();
};
function trapezoid() {
var c = Math.floor(Math.random() * 200) + 10,
d = Math.floor(Math.random() * 200) + 10,
j = Math.floor(Math.random() * 200) + 10,
i = Math.floor(Math.random() * 200) + 10;
console.log(c, d, j, i);
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + c, y);
ctx.lineTo(x + d, y - j);
ctx.lineTo(x - i, y - j);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = getRandomColor();
ctx.fill();
};
function figures() {
var div = document.getElementById('div');
div.style.top = Math.floor(Math.random() * 550) + "px";
div.style.left = Math.floor(Math.random() * 1350) + "px";
if (Math.random() < 0.2) {
rectangle();
} else if (Math.random() >= 0.2 && Math.random() < 0.4) {
circle();
} else if (Math.random() >= 0.4 && Math.random() < 0.6) {
ellipse();
} else if (Math.random() >= 0.6 && Math.random() < 0.8) {
triangle();
} else if (Math.random() >= 0.8 && Math.random() <= 1) {
trapezoid();
};
div.style.display = "block";
startTime = new Date().getTime();
};
setTimeout(figures, Math.random() * 1000);
div.onclick = function() {
var div = document.getElementById('div');
div.style.display = "none";
var finishTime = new Date().getTime();
var reactionTime = (finishTime - startTime) / 1000;
document.getElementById("timer").innerHTML = reactionTime + " seconds."
setTimeout(figures, Math.random() * 3000);
};
#div {
position: relative;
display: none;
}
#canvas {
border: 1px solid red;
}
<h3>Time of reaction <span id="timer"></span></h3>
<div id="div">
<canvas id="canvas" width="200" height="200"></canvas>
</div>
Ответы (2 шт):
Вызывая Math.random(), вы каждый раз получаете разные числа. Вам же нужно получить одно случайное число.
Конструкция с if...else if...else слишком громоздкая. Ее можно заменить на вариант покрасивее.
Можно записать все функции отрисовки фигур в массив и получать оттуда элемент со случайным индексом.
/**
* Эта функция вернет случайное число от min до max
*/
function getRandomNumber(min, max) {
return Math.floor(Math.random() * max + min);
};
/**
* Эта функция отрисует случайную фигуру
*/
function getRandomShape() {
var shapes = [rectangle, circle, ellipse, triangle, trapezoid]; // представим список всех фигур в виде массива
var randomShapeIndex = getRandomNumber(0, shapes.length); // получим случайное число от 0 до кол-ва элементов в массиве shapes
shapes[randomShapeIndex](); // обратимся к фигуре с полученным индексом и вызовем ее функцию отрисовки
}
Я не знаю, что у вас происходит в функциях rectangle, circle, ellipse, triangle, trapezoid, но могу предположить, что в div добавляется та или иная фигура.
Вот, как это работает. Фигуры я рисовать не стал, вместо них текст с названием.
(() => {
function createShape(name) {
var container = document.createElement('div');
container.textContent = name;
return container;
};
function circle() {
return createShape('circle');
};
function ellipse() {
return createShape('ellipse');
};
function triangle() {
return createShape('triangle');
};
function trapezoid() {
return createShape('trapezoid');
};
function rectangle() {
return createShape('rectangle');
};
/**
* Уберем все document.getElementById из функций, достаточно найти их документе единожды
* Запишем их в общей области видимости
*/
var div = document.getElementById('div');
var timer = document.getElementById("timer");
var startTime = getCurrentTime();
/**
* Эта функция вернет случайное число от min до max
*/
function getRandomNumber(min, max) {
return Math.floor(Math.random() * max + min);
};
/**
* Получаем текущий timestamp
*/
function getCurrentTime() {
return new Date().getTime();
}
/**
* Эта функция вернет случайную фигуру
*/
function getRandomShape() {
var shapes = [rectangle, circle, ellipse, triangle, trapezoid]; // представим список всех фигур в виде массива
var randomShapeIndex = getRandomNumber(0, shapes.length); // получим случайное число от 0 до кол-ва фигур в массиве shapes
return shapes[randomShapeIndex](); // обратимся к фигуре с полученным индексом и вызовем ее функцию отрисовки
}
function figures() {
this.innerHTML = ''; // очистим блок в чьем контексте мы выполняем функцию
var bounds = [20, 80]; // минимальное и максимальное значение границ, где может появиться кнопка
[div.style.top, div.style.left] = [bounds, bounds].map(([min, max]) => getRandomNumber(min, max) + "%"); // устанавливаем случайные значения top и left от 20 до 80
var shape = getRandomShape(); // получаем случайную фигуру
this.append(shape); // добавляем ее в блок на который нужно нажать
this.style.display = "block";
startTime = getCurrentTime();
};
/**
* Запускает отсчет со случайным таймером до вызова figures
*/
function startRandomTimeout(max, block) {
setTimeout(() => figures.call(block), getRandomNumber(0, max)); // figures вызываем в контексте block с помощью метода call, вдруг захочется добавить еще 1 кликабельный блок
}
div.onclick = function() {
this.style.display = "none";
var finishTime = getCurrentTime();
var reactionTime = (finishTime - startTime) / 1000;
timer.textContent = reactionTime + " seconds.";
startRandomTimeout(3000, this);
};
startRandomTimeout(1000, div);
})();
body {
font-family: 'Arial', sans-serif;
}
#div {
position: fixed;
display: none;
cursor: pointer;
border: 1px dashed tomato;
font-weight: bold;
}
<div id="div">Catch Me!</div>
<div id="timer"></div>
Я разбил ваш код на функции поменьше, стараясь придерживаться принципа разделения ответственностей.
UPD
Вариант с исполььзованием только canvas.
(() => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
[canvas.width, canvas.height] = [window.innerWidth, window.innerHeight];
document.body.append(canvas);
clearCanvas();
let startTime = getCurrentTime();
/**
* Эта функция вернет случайное число от min до max
*/
function getRandomNumber(min, max) {
return Math.floor(Math.random() * max + min);
};
/**
* Получаем текущий timestamp
*/
function getCurrentTime() {
return new Date().getTime();
}
function getRandomColor() {
const letters = "0123456789ABCDEF".split('');
let color = "#";
for (let i = 0; i < 6; i++ ) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
function getRandomPosition() {
const boundBorder = 200;
return [
[boundBorder, canvas.width - boundBorder*2],
[boundBorder, canvas.height - boundBorder*2]
].map(([min, max]) => getRandomNumber(min, max));
}
function rectangle() {
ctx.fillStyle = getRandomColor();
const [x, y] = getRandomPosition();
const [w, h] = [[20, 200], [20, 200]].map(([min, max]) => getRandomNumber(min, max));
ctx.fillRect(x, y, w, h);
};
function circle() {
ctx.fillStyle = getRandomColor();
const path = new Path2D();
const [x, y] = getRandomPosition();
path.arc(x, y, getRandomNumber(20, 200), 0, 2 * Math.PI);
ctx.stroke(path);
ctx.fill(path);
};
function ellipse() {
ctx.fillStyle = getRandomColor();
const path = new Path2D();
const [radiusX, radiusY] = [
getRandomNumber(20, 200),
getRandomNumber(20, 200),
];
const [x, y] = getRandomPosition();
path.ellipse(x, y, radiusX, radiusY, 50, 0, 300, false);
ctx.stroke(path);
ctx.fill(path);
};
function triangle() {
ctx.fillStyle = getRandomColor();
const path = new Path2D();
const [x, y] = getRandomPosition()
path.moveTo(x, y);
path.lineTo(x+25, y+100);
path.lineTo(x+75, y+100);
path.closePath();
ctx.stroke(path);
ctx.fill(path);
};
function trapezoid() {
const [c, d, j, i] = Array(4).fill([10, 200]).map(([min, max]) => getRandomNumber(min, max));
const path = new Path2D();
const [x, y] = getRandomPosition();
path.moveTo(x, y);
path.lineTo(x+c, y);
path.lineTo(x+d, y-j);
path.lineTo(x-i, y-j);
path.closePath();
ctx.fillStyle = getRandomColor();
ctx.stroke(path);
ctx.fill(path);
};
function clearCanvas() {
ctx.fillStyle = '#FFF';
ctx.fillRect(20, 20, canvas.width, canvas.height);
}
function clearTimer() {
ctx.fillStyle = '#FFF';
ctx.fillRect(0, 0, canvas.width, 20);
}
function drawReactionTime() {
clearTimer();
const finishTime = getCurrentTime();
const reactionTime = (finishTime - startTime) / 1000;
ctx.fillStyle = '#000';
ctx.font = "12px Arial";
ctx.fillText(`Time of reaction: ${reactionTime} sec.`, 20, 20);
}
function drawShape() {
const Shapes = [rectangle, circle, ellipse, triangle, trapezoid];
const randomIndex = getRandomNumber(0, Shapes.length);
Shapes[randomIndex]();
startTime = getCurrentTime();
}
/**
* Проверяем пиксель на закрашенность
*/
function isPositionFilled(x, y) {
for (let i = 0; i < 4; i++) {
if (ctx.getImageData(x, y, 1, 1).data[i] < 255) return true;
}
return false;
}
function startRandomTimeout(max) {
setTimeout(drawShape, getRandomNumber(0, max));
}
canvas.addEventListener('click', (event) => {
const { x, y } = event;
if (x <= 200) return;
if (!isPositionFilled(x, y)) return;
drawReactionTime();
clearCanvas();
startRandomTimeout(3000);
});
startRandomTimeout(1000);
})();
body {
position: fixed;
inset: 0;
margin: 0;
}
Есть неточности в функциях отрисовки, с ними разберетесь сами.)
Чтобы понять все моменты, рекомендую почитать данные топики на learn.javacript.ru:
После функции trapezoid() изменил код на этот и всё заработало как надо:
function getRandomNumber(min, max) {
return Math.floor(Math.random() * max + min);
};
function figures() {
var div = document.getElementById('div');
div.style.top = Math.floor(Math.random() * 550) + "px";
div.style.left = Math.floor(Math.random() * 1350) + "px";
var shapes = [rectangle, circle, ellipse, triangle, trapezoid];
var randomIndex = getRandomNumber(0, shapes.length);
shapes[randomIndex]();
div.style.display = "block";
startTime = new Date().getTime();
};
setTimeout(figures, Math.random() * 1000);
div.onclick = function() {
var div = document.getElementById('div');
ctx.clearRect(0, 0, canv.width, canv.height);
div.style.display = "none";
var finishTime = new Date().getTime();
var reactionTime = (finishTime - startTime) / 1000;
document.getElementById("timer").innerHTML = reactionTime.toFixed(3) + " seconds."
setTimeout(figures, Math.random() * 3000);
};
Спасибо всем, кто помогал :)