我制作了一个交互式艺术,但不是缩放每个子像素,而是缩放每个子像素相同的px
。
p。注释掉的代码
const WIDTH = 512
const HEIGHT = 256
const ROWS = 8
const COLUMNS = 16
const container = document.querySelector('.container')
for (let rowIndex = 0; rowIndex < ROWS; rowIndex++) {
for (let colIndex = 0; colIndex < COLUMNS; colIndex++) {
let x = normalize(colIndex, 0, COLUMNS, 0, WIDTH) + 8
let y = normalize(rowIndex, 0, ROWS, 0, HEIGHT) + 8
if (document.querySelectorAll('.dot').length != 128) {
container.appendChild(circle(x, y))
}
}
}
window.addEventListener('mousemove', handleMouseMoveEvent)
const dots = Array.from(document.querySelectorAll('.dot'))
function handleMouseMoveEvent(event) {
const relativeMousePosition = {
x: event.clientX - container.getBoundingClientRect().left,
y: event.clientY - container.getBoundingClientRect().top,
}
for (let rowIndex = 0; rowIndex < ROWS; rowIndex++) {
for (let colIndex = 0; colIndex < COLUMNS; colIndex++) {
let x = normalize(colIndex, 0, COLUMNS, 0, WIDTH) + 8
let y = normalize(rowIndex, 0, ROWS, 0, HEIGHT) + 8
const deltaX = Math.abs(x - relativeMousePosition.x)
const deltaY = Math.abs(y - relativeMousePosition.y)
const distance = Math.sqrt(deltaX ** 2 + deltaY ** 2)
let radius = normalize(distance, 0, 100, 2, 1)
radius = clamp(radius, 0, WIDTH)
for (let child = 0; child < dots.length; child++) {
dots[child].style.transform = `scale(${radius})`
}
}
}
}
function circle(x, y, radius = 16) {
const dot = document.createElement('div')
dot.className = 'dot'
dot.style.width = radius + 'px'
dot.style.height = radius + 'px'
dot.style.top = y + 'px'
dot.style.left = x + 'px'
return dot
}
function clamp(value, min = 0, max = 1) {
return Math.max(min, Math.min(max, value))
}
function normalize(number, currentScaleMin, currentScaleMax, newScaleMin = 0, newScaleMax = 1) {
const standardNormalization = (number - currentScaleMin) / (currentScaleMax - currentScaleMin)
return (newScaleMax - newScaleMin) * standardNormalization + newScaleMin
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 100vh;
display: grid;
place-items: center;
background-color: #111;
}
.container {
position: relative;
width: 512px;
height: 256px;
box-shadow: 0 0 0 1px aquamarine; /* FOR DEBUGGING */
}
.dot {
position: absolute;
border-radius: 50%;
background-color: firebrick;
transition: transform 125ms ease, top 125ms ease, left 125ms ease;
}
<div class="container"></div>
p。S -我试图制作的动画最初是在canvas
上创建的,并基于react
。但我发现canvas
相当模糊。请看这个链接,我正在尝试制作动画。
这不是我做这个动画的方式,但你所显示的代码本身有一些问题。你应该检查relativeMousePosition
x
和y
属性,当指针在.container
div
内部时触发动画。此外,您还应该清空.container
的内容。
我会告诉你一个快速修复,但你仍然需要在动画上工作。这不是一个真正的解决方案,而是一种为你指明正确方向的方法。
我建议使用CSS
transition
在width
和height
的圆圈上实现相同的结果,并使用display: none
或visibility: hidden
而不是从DOM
中添加和删除圆圈。
快速修复:
// clean up .container innerHTML
container.addEventListener('mouseleave', (e) => {
container.innerHTML = '';
});
function art(event) {
const relativeMousePosition = {
x: event.clientX - container.getBoundingClientRect().left,
y: event.clientY - container.getBoundingClientRect().top,
}
let isInsideContainer = relativeMousePosition.x > 0 && relativeMousePosition.x < 512 && relativeMousePosition.y > 0 && relativeMousePosition.y < 256;
// check the mouse position before triggering the animation
if(isInsideContainer) {
for (let rowIndex = 0; rowIndex < ROWS; rowIndex++) {
for (let colIndex = 0; colIndex < COLUMNS; colIndex++) {
let x = normalize(colIndex, 0, COLUMNS, 0, WIDTH) + 8
let y = normalize(rowIndex, 0, ROWS, 0, HEIGHT) + 8
const deltaX = Math.abs(x - relativeMousePosition.x)
const deltaY = Math.abs(y - relativeMousePosition.y)
const distance = Math.sqrt(deltaX ** 2 + deltaY ** 2)
let radius = normalize(distance, 0, 100, 32, 1)
radius = clamp(radius, 0, WIDTH)
container.appendChild(dot(x, y, radius))
}
}
}
}
在Kunal的评论之后,我添加了一个更详细的可能解决方案。这只是一个解决方案,你可能会找到其他方法来达到同样的结果,但这可能是一个好的开始。
const WIDTH = 512
const HEIGHT = 256
const ROWS = 8
const COLUMNS = 16
const PROXIMITY_THRESHOLD = Number.parseInt(WIDTH / 6, 10);
// A circle object
// represent an element in the DOM
class Circle {
constructor(position, id) {
this.position = position;
this.id = id;
}
distanceFrom(xCoordinate, yCoordinate) {
const deltaX = Math.abs(this.position.x - xCoordinate);
const deltaY = Math.abs(this.position.y - yCoordinate);
const distance = Math.sqrt(deltaX ** 2 + deltaY ** 2)
return distance;
}
isInProximity(distance) {
return distance <= PROXIMITY_THRESHOLD;
}
}
const createCircleRow = () => {
return new Array(COLUMNS)
.fill(null)
.map(() => new Circle());
};
// We create an array of arrays of circles object
const circles = new Array(ROWS).fill(null).map(createCircleRow);
const container = document.querySelector('.container')
for (let rowIndex = 0; rowIndex < ROWS; rowIndex++) {
for (let colIndex = 0; colIndex < COLUMNS; colIndex++) {
let x = normalize(colIndex, 0, COLUMNS, 0, WIDTH) + 8
let y = normalize(rowIndex, 0, ROWS, 0, HEIGHT) + 8
// Set the position and the id properties for circle object in circles Array
circles[rowIndex][colIndex].position = {x, y};
let id = `${rowIndex},${colIndex}`;
circles[rowIndex][colIndex].id = id;
if (document.querySelectorAll('.dot').length != 128) {
let circleElement = circle(x, y)
// Add an id to the circle Element
circleElement.setAttribute('id', id);
container.appendChild(circleElement);
}
}
}
function animateByPosition(xPosition, yPosition) {
circles.forEach((row) => {
row.forEach((circle) => {
let circleElement = document.getElementById(circle.id);
let distance = circle.distanceFrom(xPosition, yPosition);
if(circle.isInProximity(distance)) {
circleElement.style.visibility = 'visible';
let scaleAmount = 2 * (distance/PROXIMITY_THRESHOLD);
circleElement.style.transform = `scale(${scaleAmount})`;
} else {
circleElement.style.transform = null;
circleElement.style.visibility = 'hidden';
}
});
});
}
function hideCircles() {
circles.forEach((row) => {
row.forEach((circle) => {
let circleElement = document.getElementById(circle.id);
circleElement.style.transform = null;
circleElement.style.visibility = 'hidden';
});
});
}
window.addEventListener('mousemove', handleMouseMoveEvent);
container.addEventListener('mouseleave', hideCircles);
function handleMouseMoveEvent(event) {
const relativeMousePosition = {
x: event.clientX - container.getBoundingClientRect().left,
y: event.clientY - container.getBoundingClientRect().top,
}
let isInsideContainer = relativeMousePosition.x > 0 && relativeMousePosition.x < 512 && relativeMousePosition.y > 0 && relativeMousePosition.y < 256;
// check the mouse position before triggering the animation
if(isInsideContainer) {
animateByPosition(relativeMousePosition.x, relativeMousePosition.y);
}
}
function circle(x, y, radius = 16) {
const dot = document.createElement('div')
dot.className = 'dot'
dot.style.width = radius + 'px'
dot.style.height = radius + 'px'
dot.style.top = y + 'px'
dot.style.left = x + 'px'
return dot
}
function normalize(number, currentScaleMin, currentScaleMax, newScaleMin = 0, newScaleMax = 1) {
const standardNormalization = (number - currentScaleMin) / (currentScaleMax - currentScaleMin)
return (newScaleMax - newScaleMin) * standardNormalization + newScaleMin
}
.dot {
position: absolute;
border-radius: 50%;
background-color: firebrick;
visibility: hidden;
transition: transform 0.5s;
}