javascript - 为什么我的互动艺术在 mousemove 上无法正常工作

我制作了一个交互式艺术,但不是缩放每个孩子不同的像素,而是缩放每个孩子相同的 px

P.S 注释掉的代码

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 很模糊。请参阅此 https://www.youtube.com/watch?v=smkKv29iaZ8 了解我正在尝试制作的动画。

回答1

这不是我做这个动画的方式,但你显示的代码本身就有一些问题。您应该检查 relativeMousePosition xy 属性以在指针位于 .container div 内时触发动画。此外,您还应该清空 .container 的内容。

我将向您展示一个快速修复,但您仍然需要处理动画。这不是一个真正的解决方案,而是一种为您指明正确方向的方法。

我会建议另一种方法来实现相同的结果,使用 CSS transition 只是在圆圈的 widthheight 上,并使用 display: nonevisibility: 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;
}

相似文章

r - 如何去除每个样本的前缀

我被困在删除每个样本的前缀上。我试图删除样本中的所有数字,但这不是分组的好方法。我只想将示例名称保留为最后两个后缀。(例如:AAP-L)详情如下。先感谢您!geo$pd$title[1]"AAB-HT...

chart.js - ChartJS 多个数据集,但具有单条 chart

我需要一个使用两个不同数据集的条形chart,因为一个用于交易量,另一个用于欧元。问题是我只需要第一个柱子来使用音量values和其他4个柱子来使用欧元value,但是如果我使用两个数据集,柱子将两个...

随机推荐

最新文章