You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Vue.js实现产品动画循环并固定左下角显示2个产品

Vue.js产品图片过渡效果优化方案

问题说明

需要实现产品图片从左侧进入→停在中间→右上角消失的过渡效果,当前遇到的问题:切换到最后一个产品时,它会从右上角反向返回;同时需要始终在左下角保留最近的2个产品。

修改后的完整代码

CSS 部分

/* import font - MONTSERRAT */
@import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
html {
  font-size: 16px;
  font-family: 'Montserrat', sans-serif;
}

body .writing-vertical {
  -webkit-writing-mode: vertical-rl;
      -ms-writing-mode: tb-rl;
          writing-mode: vertical-rl;
}

body .outline-text::after {
  position: absolute;
  left: 1px;
  top: 1px;
  content: attr(data-text);
  color: white;
  z-index: -1;
  text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
}

body .outline-text-rl::after {
  position: absolute;
  left: -1px;
  top: 1px;
  content: attr(data-text);
  color: white;
  z-index: -1;
  text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
}

.product {
  position: absolute;
  transition: 1s all;
  left: 0;
  bottom: 0;
  z-index: 1;
}

.product.centerProduct {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10;
}

/* 最近的第一个历史产品(刚从中间移走) */
.product.pastProduct-1 {
  transform: translate(0, 0);
  z-index: 2;
}

/* 最近的第二个历史产品 */
.product.pastProduct-2 {
  transform: translate(-20px, 20px);
  z-index: 1;
}

/* 更早的历史产品,隐藏到视窗外 */
.product.pastProduct-hidden {
  transform: translate(-100%, 100%);
}

.product.upcomingProduct {
  /* 初始位置设到左侧外,确保从左侧进入 */
  transform: translate(-100%, 0);
}

@media (min-width: 1024px) {
  .outline-text::after {
    left: 3px !important;
    top: 5px !important;
  }
  .outline-text-rl::after {
    left: -3px !important;
    top: 5px !important;
  }
}

HTML & Vue.js 部分

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="../assets/css/style.css">
    <link rel="stylesheet" href="../assets/css/custom.css">
    <script src="https://cdn.tailwindcss.com"></script>

    <title>Document</title>
</head>

<body>
    <div id="app" class="fixed top-0 left-0 max-w-full max-h-screen min-h-screen min-w-full">
        <div v-for="(product,index) in products" :key="index"
            :class="getProductClass(product.id)"
            class="product max-w-[350px] h-[60vh] w-full">
            <div :style="{backgroundImage: 'url('+ product.src +')',backgroundSize:'cover',backgroundRepeat: 'no-repeat',backgroundPosition: 'center center'}"
                class="w-full h-full">
            </div>
        </div>
        <button @click="prev()">prev</button>
        <button @click="next()">next</button>
    </div>
    <script src="../node_modules/jquery/dist/jquery.min.js"></script>
    <script src="../assets/js/app.js"></script>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <script>
        const { createApp } = Vue

        createApp({
            data() {
                return {
                    products: [
                        { id: 0, src: "https://via.placeholder.com/1000", name: "产品 0", color: "red" },
                        { id: 1, src: "https://via.placeholder.com/1000", name: "产品 1", color: "green" },
                        { id: 2, src: "https://via.placeholder.com/1000", name: "产品 2", color: "gray" },
                        { id: 3, src: "https://via.placeholder.com/1000", name: "产品 3", color: "rebeccapurple" },
                        { id: 4, src: "https://via.placeholder.com/1000", name: "产品 4", color: "yellow" },
                        { id: 5, src: "https://via.placeholder.com/1000", name: "产品 5", color: "orangered" },
                    ],
                    activeProduct: 2,
                }
            },
            methods: {
                getProductClass(productId) {
                    const diff = this.activeProduct - productId;
                    if (diff === 0) {
                        return 'centerProduct';
                    } else if (diff === 1) {
                        return 'pastProduct-1';
                    } else if (diff === 2) {
                        return 'pastProduct-2';
                    } else if (diff > 2) {
                        return 'pastProduct-hidden';
                    } else {
                        return 'upcomingProduct';
                    }
                },
                next() {
                    this.activeProduct = (this.activeProduct + 1) % this.products.length;
                },
                prev() {
                    this.activeProduct = (this.activeProduct - 1 + this.products.length) % this.products.length;
                }
            },
        }).mount('#app')
    </script>
</body>

</html>

核心修改点

  1. 状态分类细化:将历史产品分为最近2个(pastProduct-1、pastProduct-2)和更早的产品(pastProduct-hidden),分别设置位置和层级,实现左下角层叠保留2个产品的效果。
  2. 初始位置修正:将upcomingProduct的初始位置改为左侧外(translate(-100%, 0)),确保切换时从左侧平滑进入。
  3. 切换逻辑简化:用取模运算简化next和prev的边界判断,避免循环切换时的异常过渡。
  4. 层级管理:给不同状态的产品设置z-index,确保中间产品在最上层,左下角产品按顺序层叠。

内容的提问来源于stack exchange,提问作者İsmail Cem Altekin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 21:25:18