[vue3+js]实现3d旋转效果

时间:2024-07-05 07:13:31
1. 实现效果图:

2.实现代码:

css:

<style lang="scss" scoped>
    .bottomContainer{
        width: 1200px;
        height: 400px;
        display: flex;
        justify-content: center;
        position: relative;
        margin:200px auto;
        align-items: center;
        // background-image: url("@/assets/image/test/rote_center_bg.png");
        
        background-size: 100% 100%;
        background-repeat: no-repeat;
        background-position: center;

        
        .bottomboxtest{
            position: absolute;
            width: 1000px;
            height: 1000px;
            background:radial-gradient(rgba(181, 214, 243, 0.5) 30%, rgb(108, 165, 230) 80%);
            left: 50%;
            margin-left: -500px;
            transform: rotateX(90deg);
            margin-top: -300px;
            border-radius: 50%;
        }
        .arrow-img {
            position: absolute;
            width: 22px;
            height: 22px;
            // top: calc(50% + 60px);
            top:calc(50% - 66px);
            cursor: pointer;
            z-index: 10;

            &.left {
                left: 40px;
            }

            &.right {
                right: 40px;
            }
        }
        .device-list-container{
            // position: absolute;
            display: flex;
            transform-style: preserve-3d;
            // transform: rotateX(-20deg);
            transform: rotateX(-10deg);
            width: 940px;
            height: 200px;
            .device-list {
                position: absolute;
                transform-origin: center center;
                transform-style: preserve-3d;
                transform: rotateY(-8deg);
                transition: all .8s;
                // margin-top: 75px;
                width: 940px;
                height: 400px;
                // left: 10%;
                left: -10px;
                display: flex;
                justify-content:center;
                .device-item {
                    position: absolute;
                    font-size: 16px;
                    color: #FFFFFF;
                    text-align: center;
                    margin-top: 10px;
                    .iconBox{
                        width: 80px;
                        height: 80px;
                        margin:0 auto;
                        // background: radial-gradient( circle, #E2F1FF,#A8D4FD);
                        background: #A8D4FD;

                        border-radius: 50%;
                        border: 2px solid #E5F2FF;
                        text-align: center;
                        line-height: 80px;
                        .roateIcon{
                            color:#58A0F5;
                            font-size: 32px;
                        }
                    }

                    .groupName {
                        color:#2E5480;
                        font-size: 16px;
                        margin-top:8px;
                       
                    }

                   
                }
            }    
        }
    }
</style>

html:

<div class="bottomContainer">
            
            <div class="device-list-container">
                <div class="device-list" ref="deviceListRef">
                  <div class="device-item" v-for="(item,index) in roteList">
                   
                    <div class="iconBox">
                        test
                        <!-- <i class="iconfont icon-baojing roateIcon"></i> -->
                    </div>
                    <p class="groupName">test</p>
                    
                  </div>
                  <div class="bottomboxtest"></div>
                </div>
            </div>
            <img class="arrow-img left" src="@/assets/image/test/arrow-left.png" @click="rotate(true)" alt="">
            <img class="arrow-img right" src="@/assets/image/test/arrow-right.png" @click="rotate(false)" alt="">
        </div>

js:

<script setup lang="ts">
    const roteList= ref([
        {},{},{},{},{},{}
    ]);
    const deviceListRef = ref<HTMLDivElement>();
    const imgList = ref<NodeListOf<Element>>();
    let timer: NodeJS.Timer;
    let timeout: NodeJS.Timeout;
    let current = 2;
    let angle = 360 / 4;
    let rotateDeg = 1 ;
    const previewCount = roteList.value!.length;
    onMounted(async () => {
        rotate(true);
    })
    function rotate(isPlus: boolean) {
      imgList.value = imgList.value ?? deviceListRef.value!.querySelectorAll('.device-item');
      const SIZE = imgList.value!.length;
      current += isPlus ? 1 : -1;
      current = (current + SIZE) % SIZE;
      const index = getDisplayCard(current, Math.ceil(previewCount/2), SIZE);
      angle = 360/SIZE;
      rotateDeg -= angle * (isPlus ? 1 : -1);
      deviceListRef.value!.style.transform = `rotateY(${rotateDeg}deg)`;
        // deviceListRef.value!.style.transform = `rotateY(${rotateDeg}deg)`;
      imgList.value!.forEach((v: any, i: number) => {
            (v as HTMLDivElement).style.opacity = '0';
            if (index.includes(i)) {
          (v as HTMLDivElement).style.opacity = '1';
        }
            (v as HTMLDivElement).style.transform = `rotateY(${(i - 2) * angle}deg) translateZ(370px)`;
  })
    
    function getDisplayCard(current: number, num: number, size: number) {
        const offset = Math.trunc(num / 2);
        const res = [current];
        for (let i = 1; i <= offset; i++) {
          const pos1 = current + i;
          const pos2 = current - i < 0 ? current - i + size : current - i;
          res.push(pos1, pos2);
        }
        return res.map(v => v % size);
     }
}
</script>