svg ”扫描”loading动画

780 阅读1分钟

html结构

  <svg class="loadBox" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <linearGradient id="linearGradient1" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="0%" style="stop-color:rgba(224,224,224,0.8); stop-opacity:0" />
      <stop offset="100%" style="stop-color:rgba(224,224,224,0.8); stop-opacity:1" />
    </linearGradient>
    <linearGradient id="linearGradient2" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="0%" style="stop-color:rgba(224,224,224,0.8); stop-opacity:1" />
      <stop offset="100%" style="stop-color:rgba(224,224,224,0.8); stop-opacity:0" />
    </linearGradient>
    <g transform="translate(50,50) scale(1,-1)" stroke="rgb(224,224,224)" stroke-width="1" fill="transparent">
      <circle id="c_1" cx="0" cy="0" r="50" stroke-opacity="0" />
      <circle id="c_2" cx="0" cy="0" r="42" stroke-opacity="0" />
      <circle id="c_3" cx="0" cy="0" r="34" stroke-opacity="0" />
      <g id="pointer" stroke-dasharray="81.68" stroke-linecap="round">
        <circle id="semi-circle-1" cx="0" cy="0" r="26" stroke-dashoffset="0" stroke="url(#linearGradient1)"
          stroke-width="1" fill="transparent" />
        <circle cx="0" cy="0" r="26" stroke-dashoffset="81.68" stroke="url(#linearGradient2)" stroke-width="1"
          fill="transparent" />
      </g>
      <g id="wifiIcon" transform="translate(0,-5) rotate(45) scale(1,1)" stroke-linecap="round">
        <circle cx="0" cy="0" r="1" stroke-width="0" stroke="red" stroke-opacity="0.8" fill="rgb(224,224,224)" />
        <circle cx="0" cy="0" r="4" stroke-dasharray="6.28,18.85" stroke-width="1" stroke="rgb(224,224,224)"
          stroke-opacity="1" fill="transparent" />
        <circle cx="0" cy="0" r="8" stroke-dasharray="12.57,37.70" stroke-width="1" stroke="rgb(224,224,224)"
          stroke-opacity="1" fill="transparent" />
        <circle cx="0" cy="0" r="12" stroke-dasharray="18.85,56.55" stroke-width="1" stroke="rgb(224,224,224)"
          stroke-opacity="1" fill="transparent" />
      </g>
    </g>
  </svg>

css 部分

.loadBox {
  --loadFrom: 360deg;
  --loadTo: 0deg;
  --loadAnimation: load 2s linear infinite;
  --delayTime: 700ms;
  width: 300px;
  height: 300px;
  overflow: visible;
  cursor: pointer;
}
@keyframes load {
  from {
    transform: rotate(var(--loadFrom));
  }
  to {
    transform: rotate(var(--loadTo));
  }
}
#pointer {
  animation: var(--loadAnimation);
}
@keyframes scaleAni {
  0% {
    r: 30;
    stroke-opacity: 0.1;
  }
  50%{
    stroke-opacity: 0.5;
  }
  100% {
    r: 50;
    stroke-opacity: 0;
  }
}
#c_1 {
  animation: scaleAni calc(var(--delayTime) * 3) ease-out infinite;
  animation-delay: calc(var(--delayTime) * 0);
}
#c_2 {
  animation: scaleAni calc(var(--delayTime) * 3) ease-out infinite;
  animation-delay: calc(var(--delayTime) * 1);;
}
#c_3 {
  animation: scaleAni calc(var(--delayTime) * 3) ease-out infinite;
  animation-delay: calc(var(--delayTime) * 2);
}

注意:在vue项目中,loading中间部分动画消失了,var(--loadAnimation)设置无效; 试验多次,写法没问题,因为我项目使用了sass,需要去掉scoped字段,即: <style lang="scss" scoped></style>换成<style lang="scss"></style>

javascript 部分

  // const handle = (r, p) => {
//   const zhouchang = 2 * Math.PI * r;
//   return `${(zhouchang * p).toFixed(2)},${(zhouchang * (1 - p)).toFixed(2)}`;
// };
// console.log(handle(4, 0.25), handle(8, 0.25), handle(12, 0.25));
const namespace = "http://www.w3.org/2000/svg";
const dom_svg = document.getElementsByTagNameNS(namespace, `svg`)[0];
const dom_pointer = document.querySelector("#pointer");
const conversionRotate = (matrix) => {
  let aa = Math.round((180 * Math.asin(matrix[0])) / Math.PI);
  let bb = Math.round((180 * Math.acos(matrix[1])) / Math.PI);
  let cc = Math.round((180 * Math.asin(matrix[2])) / Math.PI);
  let dd = Math.round((180 * Math.acos(matrix[3])) / Math.PI);
  let deg = 0;
  if (aa == bb || -aa == bb) {
    deg = dd;
  } else if (-aa + bb == 180) {
    deg = 180 + cc;
  } else if (aa + bb == 180) {
    deg = 360 - cc || 360 - dd;
  }
  return deg >= 360 ? 0 : deg;
};
const getCurrentRotate = () => {
  const st = getComputedStyle(dom_pointer, null);
  const transformVal = st.getPropertyValue("transform");
  let matrixArr = "";
  let res = "0";
  if (transformVal !== "none") {
    matrixArr = transformVal.split("(")[1].split(")")[0].split(",");
    res = conversionRotate(matrixArr);
  }
  return res;
};
const handleStart = () => {
  const rotateVal = getCurrentRotate();
  dom_pointer.style.transform = `rotate(${rotateVal}deg)`;
  dom_svg.style.setProperty(`--loadAnimation`, `none`);
};
const handleStop = () => {
  const rotateVal = getCurrentRotate();
  dom_svg.style.setProperty(`--loadFrom`, `${rotateVal}deg`);
  dom_svg.style.setProperty(`--loadTo`, `${rotateVal - 360}deg`);
  dom_svg.style.setProperty(`--loadAnimation`, `load 2s linear infinite`);
}
dom_svg.addEventListener(`mousedown`, (e) => {
  handleStart();
});
dom_svg.addEventListener(`mousemove`, (e) => {});
dom_svg.addEventListener(`mouseup`, (e) => {
  handleStop();
});

效果图:

loading.gif

codepen链接:codepen.io/maomaoliang…