javascript 尝试使用CSS / JS或animeJS等库的帮助来动画化环周围的图像

dba5bblo  于 2023-03-16  发布在  Java
关注(0)|答案(2)|浏览(140)

除了一些基本的过渡,我几乎是一个新手,当谈到动画在css/JS。为一个项目,下周开始,我已经提交了一个特定的动画,我觉得有点挑战性。这里的动画解释:

  • 应该有10-30个图像围绕环/圆旋转/循环。
  • 图像也应该随机地有一个小的偏移远离环,以给予一个“分散”的效果
  • 图像应在环周围展开以“填充”环。
  • 图像应具有“反弹”效果或类似效果

我希望达到的效果:

目前为止我尝试的方法是:我一直在研究一些动画的JS库。其中最突出的是animeJS(https://animejs.com/)和MoJS(https://mojs.github.io/)。我决定测试一下animeJS。
下面是一个CodePen:CodePen

const imgContainer = document.getElementById("imgContainer");
const svgContainer = document.getElementById("svgContainer");

const imgURL =
  "https://images.unsplash.com/photo-1678833823181-ec16d450d8c1?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=774&q=80";

function generateCircleWithRandomSizes(index) {
  const randomSize = generateRandomNumberBetween(450, 520);
  return `<div class="svgWrapper">
<svg width="${randomSize}" height="${randomSize}" fill="none" stroke="none" id="svgContainer">
            <path id="path${index + 1}" fill="none" d="
                m ${(800 - randomSize) / 2}, ${800 - randomSize}
                a 1,1 0 1,1 ${randomSize},0
                a 1,1 0 1,1 -${randomSize},0
                " />
        </svg>
</div>`;
}

function generateRandomNumberBetween(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}

for (let i = 0; i < 30; i++) {
  imgContainer.innerHTML += `<img class="image" id="img${i + 1}" src="${imgURL}" />`;

  svgContainer.innerHTML += generateCircleWithRandomSizes(i);

  let path = anime.path(`#path${i + 1}`);

  anime({
    targets: `#img${i + 1}`,
    translateX: path("x"),
    translateY: path("y"),
    easing: "linear",
    duration: 10000,
    loop: true
  });
}

这是我目前正在尝试的方法,但是正如CodePen所显示的,我遇到了很多问题。我正在生成尽可能多的圆圈,因为我有图像,并希望每个路径的目标1个图像。这种方法将给予我的分散效果,我试图得到。然而,正如你所看到的,似乎animeJS只动画的图像之一,而且它似乎遵循路径,但是偏移到左上角。我假设这与我的CSS和我如何“居中”SVG/Path有关,这最终显示了我的另一个问题。我不太确定如何动态生成这些环并始终将它们居中。
我有一点困难的时间把我的手指放在如何最好地解决这个问题。我是使用最好的库为这个用例?我甚至需要一个库?我应该得到它从一个完全不同的Angular ?
我真的很想得到一些帮助。

o8x7eapl

o8x7eapl1#

这里有一个简单的方法,它不做“反弹”或小的偏移,但这应该是一个很好的开端。

var radius = 150;
var offsetX = 300;
var offsetY = 200;
var offsetA = 0;

setInterval(updateSpin, 30);

updateSpin();

function updateSpin(){  
  var imgs = document.querySelectorAll( '#imgContainer img' );
  

    // Evenly distribute images around circle
  slice = 360.0 / imgs.length; 

    imgs.forEach((e,index) => {
    // Convert angle to radians
    radians = (slice*index + offsetA) * (Math.PI/180);
    
    // Some simple pythagora geometry
    x = offsetX + Math.cos(radians) * radius;
    y = offsetY + Math.sin(radians) * radius;
    
    // Update image position
    e.style.top  = y+'px';
    e.style.left = x+'px';
  });

    // Increment angle to images rotate on next update
    offsetA += 0.5;
}
.container{background:white;}

.imgContainer img{position:fixed;width:100px;height:80px}
<div class="container">
  <div class="inner">
    <div class="imgContainer" id="imgContainer">
      <img src="">
      <img src="">
      <img src="">
      <img src="">
      <img src="">
      <img src="">
      <img src="">
    </div>
  </div>
</div>
xriantvc

xriantvc2#

您当前的anime.js代码只对最后一个元素进行了动画处理,因为您使用innerHTML()打破了前面的元素绑定。
作为一种替代方法,您可以使用insertAdjacentHTML(),如“是否可以在不破坏后代的事件侦听器的情况下将其附加到innerHTML?”

imgContainer.insertAdjacentHTML('beforeend', `<img class="image" id="img${
    i + 1}" src="${imgURL}" />`) ;

  svgContainer.insertAdjacentHTML('beforeend', generateCircleWithRandomSizes(i));

示例:anime.js

x一个一个一个一个x一个一个二个一个x一个一个三个一个

备选方案:SVG SMIL <AnimateMotion>

const ns = "http://www.w3.org/2000/svg";
const svgContainer = document.getElementById("svgContainer");
const svgEl = document.getElementById("svg");
const defs = document.getElementById("defs");
const duration = 6;

// image array
let images = [
  { src: "https://placehold.co/100x100/green/FFF", width: 100, height: 100 },
  { src: "https://placehold.co/100x100/orange/FFF", width: 100, height: 100 },
  { src: "https://placehold.co/100x100/red/FFF", width: 100, height: 100 }
];

for (let i = 0; i < images.length; i++) {
  
  // generate random motion paths
  generateCircleWithRandomSizes(i);
  
  // create svg <image> elements
  let img = images[i];
  let newImage = document.createElementNS(ns, "image");
  newImage.setAttribute("x", -img.width / 2);
  newImage.setAttribute("y", -img.height / 2);
  newImage.setAttribute("width", img.width);
  newImage.setAttribute("height", img.height);
  newImage.setAttribute("href", img.src);
  newImage.id = `img${i}`;

  // define animation
  let animateMotion = document.createElementNS(ns, "animateMotion");
  animateMotion.setAttribute("begin", `-${(duration / images.length) * i} `);
  animateMotion.setAttribute("dur", `${duration}`);
  animateMotion.setAttribute("repeatCount", "indefinite");
  let mpath = document.createElementNS(ns, "mpath");
  mpath.setAttribute("href", `#path${i}`);

  // append elements
  animateMotion.appendChild(mpath);
  newImage.appendChild(animateMotion);
  svg.appendChild(newImage);

}

function generateCircleWithRandomSizes(index) {
  let randomSize = generateRandomNumberBetween(400, 500);
  let newMpath = document.createElementNS(ns, "path");
  let d = `M ${(800 - randomSize) / 2}, ${800 - randomSize}
                a 1,1 0 1,1 ${randomSize},0
                a 1,1 0 1,1 -${randomSize},0z`;
  newMpath.setAttribute("d", d);
  newMpath.id = `path${index}`;
  defs.appendChild(newMpath);
}

function generateRandomNumberBetween(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}
svg{
  width:50%;
  border:1px solid #ccc
}

image{
   animation: 0.5s bouncing forwards infinite;
}

@keyframes bouncing {
  0% {
    transform: scale(1)
  }
  50% {
    transform: translate(10px, 10px) scale(1)
  }
  100% {
    transform: scale(1)
  }
}
<div id="svgContainer">
      <svg viewBox="0 0 800 800" fill="none" stroke="#000" id="svg">
        <g id="defs"></g>
      </svg>
    </div>

在本例中,您可以将图像作为<image>元素附加到<svg>元素。

<!-- define motion path -->
<defs>
  <path d="M 175, 350a 1,1 0 1,1 450,0a 1,1 0 1,1 -450,0z" id="path0"/>
</defs>
<image x="-50" y="-50" width="100" height="100" href="https://placehold.co/100x100/green/FFF" id="img0">
    <animateMotion begin="-10 " dur="6" repeatCount="indefinite">
        <!-- reference motion path -->
        <mpath href="#path0"></mpath>
    </animateMotion>
</image>

路径偏移可以通过负begin值来实现。
如此处“偏移SVG AnimateMotion的动画起点”所述。

备选方案2:offset-path

免责声明:目前许多浏览器(尤其是webkit /safari)尚未完全实现

const duration = 6;

// image array
let images = [
  { src: "https://placehold.co/100x100/green/FFF", width: 100, height: 100 },
  { src: "https://placehold.co/100x100/orange/FFF", width: 100, height: 100 },
  { src: "https://placehold.co/100x100/red/FFF", width: 100, height: 100 }
];

for (let i = 0; i < images.length; i++) {
  //generate random motion paths
  let d = generateCircleWithRandomSizes(i, 400, 520);

  // create svg <image> elements
  let img = images[i];
  let newImage = document.createElement("img");
  newImage.classList.add('image');
  newImage.setAttribute("width", img.width);
  newImage.setAttribute("height", img.height);
  newImage.setAttribute("src", img.src);
  newImage.id = `img${i}`;

  // append elements
  imgContainer.appendChild(newImage);
  
  // define offset path
  newImage.style["offset-path"] = `path('${d}')`;  
  newImage.style["offset-rotate"] = `0deg`;
  let delay = (100 / images.length) * i;

  newImage.animate(
    [
      { offsetDistance: `${0 + delay}%` },
      { offsetDistance: `${100 + delay}%` }
    ],
    {
      duration: duration*1000,
      iterations: Infinity
    }
  );
}

function generateCircleWithRandomSizes(index, r1=400, r2=500) {
  let randomSize = generateRandomNumberBetween(r1, r2);
  let d = `M ${(800 - randomSize) / 2}, ${
    800 - randomSize
  }a 1,1 0 1,1 ${randomSize},0a 1,1 0 1,1 -${randomSize},0z`;
  return d;
}

function generateRandomNumberBetween(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}
svg{
  width:50%;
  border:1px solid #ccc
}

.image{
  position:absolute;
  animation: 0.5s bouncing forwards infinite;
}

@keyframes bouncing {
  0% {
    transform: scale(1)
  }
  50% {
    transform: translate(10px, 10px) scale(1)
  }
  100% {
    transform: scale(1)
  }
}
<div class="imgContainer" id="imgContainer">
</div>

我们希望在不久的将来能看到更好的支持。主要好处:

  • 在css中指定一个运动路径-不需要svg元素。
  • 在css中通过offset-distance属性可以很容易地控制起始偏移。

相关问题