当前位置: 首页 > article >正文

前端炫酷动画--图片(一)

目录

一、四角线框的跟随移动

二、元素倒影(-webkit-box-reflect)

三、模特换装(mask+blend)

四、元素平滑上升

五、无限视差滚动

六、判断鼠标进入方向(轮播方向)

七、环形旋转效果

八、黑白小球交替旋转

九、hover时圆形放大

十、画一棵随机树(canvas)

十一、代码雨效果(canvas)

一、四角线框的跟随移动

    <style>
      body {
        background: black;
      }
      .container {
        width: 400px;
        height: 40vh;
        position: relative;
        display: grid; /* 使用 Grid 布局 */
        grid-template-columns: repeat(2, 1fr); /* 设置两列 */
        grid-gap: 20px; /* 设置网格项之间的间距 */
        justify-items: center;
        align-items: center;
        margin: 20px;
      }
      .pointer {
        position: absolute;
        --l: 30px; /* 长度 */
        --g: 15px; /* 间隔 */
        --t: 3px; /* 粗细 */
        --s: 394px; /* 框住的大小 */
        --x: 0px;
        --y: 0px;
        width: calc(var(--s) + var(--g) * 2);
        height: calc(var(--s) + var(--g) * 2);
        border: var(--t) solid #fff;
        left: calc(var(--x) - var(--g));
        top: calc(var(--y) - var(--g));
        transition: 0.5s;
        /* 圆锥渐变(conic-gradient)作为遮罩效果 */
        -webkit-mask: conic-gradient(
            at var(--l) var(--l),
            transparent 75%,
            red 75%
          )
          0 0 / calc(100% - var(--l)) calc(100% - var(--l));
      }
    </style>
  <body>
    <div class="container">
      <div class="pointer"></div>
      <div class="item">
        <img src="https://picsum.photos/id/371/400/400" alt="" />
      </div>
      <div class="item">
        <img src="https://picsum.photos/id/372/400/400" alt="" />
      </div>
      <div class="item">
        <img src="https://picsum.photos/id/374/400/400" alt="" />
      </div>
      <div class="item">
        <img src="https://picsum.photos/id/376/400/400" alt="" />
      </div>
    </div>
    <script>
      const imgs = document.querySelectorAll(".container img");
      const pointer = document.querySelector(".pointer");
      for (const img of imgs) {
        img.onmouseenter = () => {
          pointer.style.setProperty("--x", img.offsetLeft + "px");
          pointer.style.setProperty("--y", img.offsetTop + "px");
          pointer.style.setProperty("--s", img.offsetWidth + "px");
        };
      }
    </script>
  </body>

二、元素倒影(-webkit-box-reflect)

      .card {
        box-shadow: 0 0 8px #fff;
        width: 200px;
        -webkit-box-reflect: below 15px
          linear-gradient(transparent, transparent, #0005);
      }

三、模特换装(mask+blend)

前提:准备一张白色完整图片和要换装的衣服块图片,后期再混合

    <style>
      .card {
        width: 300px;
        height: 500px;
        position: relative;
      }
      .source {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
      }
      .skirt {
        position: absolute;
        inset: 0; /*等同于 left:0;top:0;bottom: 0;right: 0; */
        background:red;
        --mask:url(./partImg.png) 50% 50% / cover;
        /* 将用蒙版将衣服块“染成”背景色 */
        mask:var(--mask);
        -webkit-mask:var(--mask);
        /* 混合衣服块和背景色 */
        mix-blend-mode: multiply;
      }
    </style>
  <body>
    <div class="card">
      <img src="./whiteImg.png" alt="" class="source" />
      <div class="skirt"></div>
    </div>
  </body>

四、元素平滑上升

// useSlideIn.js
const DISTANCE = 150;
const DURATION = 500;
const map = new WeakMap();
const ob = new IntersectionObserver((entries) => {
  for (const entry of entries) {
    if (entry.isIntersecting) {
      // 该元素和视口相交 播放该元素的动画
      const animation = map.get(entry.target);
      if (animation) {
        animation.play();
        ob.unobserve(entry.target); // 播放一次,取消观察
      }
    }
  }
});
function isBelowViewport(el) {
  const rect = el.getBoundingClientRect();
  return rect.top - DISTANCE > window.innerHeight;
}
export default {
  mounted(el) {
    if (!isBelowViewport(el)) {
      return;
    }
    const animation = el.animate(
      [
        {
          transform: `translateY(${DISTANCE}px)`,
          opacity: 0.5
        },
        {
          transform: 'translateY(0)',
          opacity: 1
        }
      ],
      {
        duration: DURATION,
        easing: 'ease-in-out',
        fill: 'forwards'
      }
    );
    animation.pause();
    ob.observe(el);
    map.set(el, animation);
  },
  unmounted(el) {
    ob.unobserve(el);
  }
};
<template>
  <div>
    <div v-slide-in class="item" v-for="n in 10" :key="n">{
  
  { n }}BOX</div>
  </div>
</template>
<script>
import slideIn from './useSlideIn';
export default {
  directives: {
    'slide-in': slideIn
  },
};
</script>

五、无限视差滚动

  <style>
    .scroll-container {
      display: flex;
      overflow: hidden;
      position: relative;
      height: 400px; /* 设置容器高度 */
    }
    .item {
      position: absolute;
      width: 100%;
      height: 100%;
      transition: transform 0.5s ease;
    }
    .item img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    .scroll-down .cur {
      transform: translateY(100%);
    }
    .scroll-up .cur {
      transform: translateY(-100%);
    }
  </style>
<body>
  <div class="scroll-container"></div>
  <script>
    const imgs = [
      "https://picsum.photos/id/376/800/800",
      "https://picsum.photos/id/372/800/800",
      "https://picsum.photos/id/373/800/800",
      "https://picsum.photos/id/374/800/800",
      "https://picsum.photos/id/375/800/800"
    ];
    const container = document.querySelector(".scroll-container");
    let curIndex = 0;
    function getPrevIndex() {
      return curIndex === 0 ? imgs.length - 1 : curIndex - 1;
    }
    function getNextIndex() {
      return curIndex === imgs.length - 1 ? 0 : curIndex + 1;
    }
    function createElement(i) {
      const div = document.createElement("div");
      div.className = "item";
      const img = document.createElement("img");
      img.src = imgs[i];
      div.appendChild(img);
      container.appendChild(div);
      return div;
    }
    function resetElements() {
      container.innerHTML = "";
      const prevIndex = getPrevIndex();
      const nextIndex = getNextIndex();
      createElement(prevIndex).classList.add("prev");
      const curItem = createElement(curIndex);
      curItem.classList.add("cur");
      createElement(nextIndex).classList.add("next");
    }
    resetElements();
    let isAnimation = false;
    window.addEventListener("wheel", (e) => {
      if (!e.deltaY || isAnimation) {
        return;
      }
      isAnimation = true;
      if (e.deltaY > 0) {
        curIndex = getNextIndex();
        container.classList.add("scroll-down");
      } else {
        curIndex = getPrevIndex();
        container.classList.add("scroll-up");
      }
    });
    container.addEventListener("transitionend", () => {
      container.classList.remove("scroll-down");
      container.classList.remove("scroll-up");
      isAnimation = false;
      resetElements();
    });
  </script>
</body>

六、判断鼠标进入方向(轮播方向)

    <script>
      const container = document.querySelector(".container");
      const rect = container.getBoundingClientRect();
      const theta = Math.atan2(rect.height, rect.width);
      container.addEventListener("mouseenter", (e) => {
        const x = e.offsetX - rect.width / 2;
        const y = rect.height / 2 - e.offsetY;
        const d = Math.atan2(y, x);
        if (d < theta && d >= -theta) {
          container.classList.add("right");
        } else if (d >= theta && d < Math.PI - theta) {
          container.classList.add("top");
        } else if (d >= Math.PI - theta || d < -(Math.PI - theta)) {
          container.classList.add("left");
        } else {
          container.classList.add("bottom");
        }
      });
      container.addEventListener("mouseleave", () => {
        container.className = "container";
      });
    </script>

七、环形旋转效果

$size: 300px;
$imgSize: 80px;
.container {
  width: $size;
  height: $size;
  outline: 1px solid #000;
  margin: 0 auto;
  position: relative;
  margin-top: 60px;
  display: flex;
  justify-content: center;
  align-items: start;
  border-radius: 50%;
  animation: rotation 20s linear infinite;
  @keyframes rotation {
    to {
      transform: rotate(calc(360deg - var(--initDeg, 0deg)));
    }
  }
  .item {
    width: $imgSize;
    height: $imgSize;
    position: absolute;
    margin-top: -40px;
    img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      border-radius: 50%;
    }
  }
}
$n: 5;
$pDeg: 360deg / $n;
.item {
  transform-origin: center $size / 2 + $imgSize / 2;
  @for $i from 1 through $n {
    $deg: $pDeg * ($i - 1);
    &:nth-child(#{$i}) {
      transform: rotate($deg);
      img {
        --initDeg:#{$deg};
        transform: rotate(-$deg); //将歪斜的图片矫正
        animation: rotation 20s linear infinite reverse;
      }
    }
  }
}

八、黑白小球交替旋转

  <div class="loading">
    <!-- 快捷键:div.dot*36 -->
  </div>
body {
  background: #66c7f4;
}
$ballSize: 10px; //小球尺寸
$containerSize: 150px; //容器尺寸
$n: 36;
$pDeg: 360deg / $n;
$d:2s;
.loading {
  width: $containerSize;
  height: $containerSize;
  margin: 50px auto;
  position: relative;
  border-radius: 50%;
  // outline: 1px solid #fff;
}
.dot {
  position: absolute;
  left: 50%;
  top: 0;
  width: $ballSize;
  height: $ballSize;
  margin-left: -$ballSize / 2;
  margin-top: -$ballSize / 2;
  perspective: 70px;
  // background: #f40;
  transform-origin: center $containerSize / 2 + $ballSize / 2;
  perspective: 70px;
  transform-style: preserve-3d;
  @for $i from 1 through $n {
    &:nth-child(#{$i}) {
      transform: rotate($pDeg * ($i - 1));
      &::before,
      &::after {
        animation-delay: -$d / $n * ($i - 1) * 6;
      }
    }
    &::before,
    &::after {
      content: "";
      position: absolute;
      width: 100%;
      height: 100%;
      border-radius: 50%;
    }
    &::before {
      background: #000;
      top: -100%;
      animation: rotation-black $d infinite;
      @keyframes rotation-black {
        0% {
          animation-timing-function: ease-in;
        }
        25% {
          transform: translate3d(0, 100%, $ballSize);
          animation-timing-function: ease-out;
        }
        50% {
          transform: translate3d(0, 200%, 0);
          animation-timing-function: ease-in;
        }
        75% {
          transform: translate3d(0, 100%, -$ballSize);
          animation-timing-function: ease-out;
        }
      }
    }
    &::after {
      background: #fff;
      top: 100%;
      animation: rotation-white $d infinite;
      @keyframes rotation-white {
        0% {
          animation-timing-function: ease-in;
        }
        25% {
          transform: translate3d(0, -100%, -$ballSize);
          animation-timing-function: ease-out;
        }
        50% {
          transform: translate3d(0, -200%, 0);
          animation-timing-function: ease-in;
        }
        75% {
          transform: translate3d(0, -100%, $ballSize);
          animation-timing-function: ease-out;
        }
      }
    }
  }
}

九、hover时圆形放大

    <style>
      .avatar {
        width: 200px;
        height: 200px;
        border-radius: 50%;
        background: url("./qiang.jpg");
        cursor: pointer;
        position: relative;
      }
      .avatar::before,
      .avatar::after {
        content: "";
        position: absolute;
        inset: 0;
        border-radius: 50%;
      }
      .avatar::before {
        background: rgba(0, 0, 0, 0.5);
      }
      .avatar::after {
        background: inherit; /* 继承自父元素 */
        clip-path: circle(0% at 50% 50%);
        transition: .3s;
      }
      .avatar:hover::after {
        clip-path: circle(50% at 50% 50%);
      }
    </style>
  <body>
    <div class="avatar"></div>
  </body>

十、画一棵随机树(canvas)

  <body>
    <canvas id="bg"></canvas>
    <script>
      const cvs = document.getElementById("bg");
      const ctx = cvs.getContext("2d");
      cvs.width = window.innerWidth;
      cvs.height = window.innerHeight;
      // 更改坐标原点
      ctx.translate(cvs.width / 2, cvs.height);
      ctx.scale(1, -1);
      // 画树干
      drawBranch([0, 0], 200, 30, 90);
      function drawBranch(v0, length, thick, dir) {
        if (thick < 10 && Math.random() < 0.3) {
          return;
        }
        if (thick < 2) {
          ctx.beginPath();
          ctx.arc(...v0, 10, 0, 2 * Math.PI);
          ctx.fillStyle = "red";
          ctx.fill();
          return;
        }
        ctx.beginPath();
        ctx.moveTo(...v0);
        const v1 = [
          v0[0] + length * Math.cos((dir * Math.PI) / 180),
          v0[1] + length * Math.sin((dir * Math.PI) / 180),
        ];
        ctx.lineTo(...v1);
        ctx.lineWidth = thick;
        ctx.fillStyle = "#333";
        ctx.lineCap = "round";
        ctx.stroke();
        // 递归调用画左右两边的树枝
        drawBranch(v1, length * 0.8, thick * 0.8, dir + Math.random() * 30);
        drawBranch(v1, length * 0.8, thick * 0.8, dir - Math.random() * 30);
      }
    </script>
  </body>

十一、代码雨效果(canvas)

  <body>
    <canvas id="bg"></canvas>
    <script>
      const cvs = document.getElementById("bg");
      const width = window.innerWidth * devicePixelRatio,
        height = window.innerHeight * devicePixelRatio;
      // 设置canvas尺寸为窗口尺寸
      cvs.width = width;
      cvs.height = height;
      const ctx = cvs.getContext("2d");
      const fontSize = 20 * devicePixelRatio;
      const columnWidth = fontSize; //列宽
      const columnCount = Math.floor(width / columnWidth); //列的数量
      const nextChar = new Array(columnCount).fill(0); //每列下一个文字是第几个文字
      // 获取随机颜色 (HEX 格式)
      function getRandomColor() {
        const r = Math.floor(Math.random() * 256);
        const g = Math.floor(Math.random() * 256);
        const b = Math.floor(Math.random() * 256);
        // 将每个颜色分量转换为两位的十六进制格式,并拼接成 HEX 颜色值
        return `#${((1 << 24) | (r << 16) | (g << 8) | b)
          .toString(16)
          .slice(1)
          .toUpperCase()}`;
      }
      // 获取随机字符
      function getRandomChar() {
        const chars =
          "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        const randomIndex = Math.floor(Math.random() * chars.length);
        return chars[randomIndex];
      }
      function draw() {
        ctx.fillStyle = "rgba(0,0,0,0.1)";
        ctx.fillRect(0, 0, width, height);
        for (let i = 0; i < columnCount; i++) {
          const char = getRandomChar();
          ctx.fillStyle ='green';//赋值为getRandomColor()就是随机彩色
          ctx.font = `${fontSize}px "Roboto Mono"`;
          const x = columnWidth * i;
          const index = nextChar[i];
          const y = (index + 1) * fontSize;
          ctx.fillText(char, x, y);
          if (y > height && Math.random() > 0.99) {
            nextChar[i] = 0;
          } else {
            nextChar[i]++;
          }
        }
      }
      draw();
      setInterval(draw, 40);
    </script>
  </body>


http://www.kler.cn/a/511809.html

相关文章:

  • wps数据分析000002
  • AIGC视频生成模型:Meta的Emu Video模型
  • elasticsearch基础
  • 数据结构入门
  • CT重建笔记(三)——共轭梯度法
  • ASP .NET Core 学习(.NET9)配置接口访问路由
  • 2024年博客之星主题创作|猫头虎分享AI技术洞察:2025年AI发展趋势前瞻与展望
  • 火狐浏览器Firefox一些配置
  • C# 可空值类型
  • 在视频汇聚平台EasyNVR平台中使用RTSP拉流的具体步骤
  • Kotlin基础知识学习(三)
  • Vue3 nginx 打包后遇到的问题
  • 数据结构——AVL树的实现
  • FPGA与ASIC:深度解析与职业选择
  • IOS 安全机制拦截 window.open
  • vector扩容 list和vector的比较
  • Kotlin 2.1.0 入门教程(六)
  • Windows上同时配置GitHub和Gitee服务
  • MySQL left join联合查询(on)
  • 用公网服务器实现内网穿透
  • WPF 实现可视化操作数据库的程序全解析
  • 【MySQL篇】使用mysqldump导入报错Unknown collation: ‘utf8mb4_0900_ai_ci‘的问题解决
  • excel实用工具
  • 易答题PHP通用工资条成绩单自动排版打印工具V1.0
  • 大模型GUI系列论文阅读 DAY2:《ScreenAgent:一种基于视觉语言模型的计算机控制代理》
  • CycleGAN - CycleGAN网络:无监督图像到图像转换的生成对抗网络