vue实现若干个规则排列的小盒子点击时使其变色同时动画移动阴影大盒子跟随在其中间位置

提示:文章后面附有demo的所有代码,有需要可以直接跳过浏览

功能展示:

小盒子的动态点击移动


个人思路

  1. 先拿到对应小盒子位置,并给小盒子变色
  2. 设置阴影盒子的宽度(这里设置为五个小盒子的宽度)
  3. 获取小盒子的位置和宽度,计算阴影盒子的位置
  4. 分小盒子点击的位置条件进行判断,分别为前三个、后三个小盒子和其它中间位置的小盒子(是为避免阴影盒子顶出画面的情况,例如有20个小盒子,当点击第1、2、3个盒子时,阴影盒子始终应该靠左边框,后三个盒子同理)

提示:以下是本篇文章正文内容,若有缺漏之处欢迎补充

一、数据结构

需要用require将图片的相对路径包起来才能显示图片,否则效果会像视频中第三个图片一样

因为在JavaScript中,使用require是一种常见的方式来加载模块或文件。当你使用相对路径加载一个图片时,如果没有使用require,浏览器会尝试从当前页面的URL基础上解析图片的相对路径,这可能会导致找不到图片或者加载出错。
使用require可以确保代码在Node.js环境下正常工作,并且还可以防止一些潜在的安全问题,例如,使用require可以防止注入攻击,因为它只允许你引用已存在于本地文件系统中的文件,而不是从Web上动态加载文件。此外,使用require可以确保你的代码可移植且易于维护,因为你只需要改变文件的位置而不需要修改代码中的路径信息。

dataArray: [
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-01'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-02'
                },
                {
    
    
                    url: './images/excel.jpg',//不会显示
                    name: 'abcd-03'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-04'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-05'
                },
                {
    
    
                    url: require('./images/excel.jpg'),
                    name: 'abcd-06'
                },
                {
    
    
                    url: require('./images/excel.jpg'),
                    name: 'abcd-07'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-08'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-09'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-10'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-11'
                },
                {
    
    
                    url: require('./images/excel.jpg'),
                    name: 'abcd-12'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-13'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-14'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-15'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-16'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-17'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-18'
                },
                {
    
    
                    url: require('./images/excel.jpg'),
                    name: 'abcd-19'
                },
                {
    
    
                    url: require('./images/excel.jpg'),
                    name: 'abcd-20'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-21'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-22'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-23'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-24'
                },
],

二、dom结构

使用循环遍历item项,并赋予每个item都有不一致的id名称(:id=“‘menuOnly’+index”),便于之后拿到对应的项进行操作

<div class="bottom-class">
            <div class="shadow-class" id="pic"></div>
            <div class="outbox-class">
                <div v-for="(item,index) in dataArray" :key="index" :id="'menuOnly'+index" @click="selectOnlyFileV2(index,item)" class="box-class">
                    <div class="state-class">
                        <img v-bind:src="item.url" alt="" style="width: 85%; height: 85%">
                    </div>
                    <div class="stationnum-class">
                        {
   
   {item.name.slice(item.name.length-2,item.name.length)}}
                    </div>
                </div>
            </div>
</div>

三、js部分

1.初始化读入数据

代码如下(示例):

mounted() {
    
    
    this.selectOnlyFileV2(0, this.dataArray[0]);//默认初始为第一个
},

2.method方法

拿到点击事件中对应的索引和item项

selectOnlyFileV2(i, item) {
    
    
            /**这里编辑操作代码 */
            // this.handleClickV1(i);
            this.handleClickV2(i);
            // this.handleClickV3(i);
},

1.handleClickV1()方法

点击并移动阴影盒子

handleClickV1(i) {
    
    
            window.requestAnimationFrame(function () {
    
    //不需要等待异步执行完成--会导致与.shadow-class不同步,弃用
                $("#menuOnly" + i).addClass("select").siblings().removeClass("select");
            });
            setTimeout(() => {
    
    
                $(".shadow-class").width($("#menuOnly0").outerWidth() * 5);
                // $("#menuOnly" + i).addClass("select").siblings().removeClass("select");
                if (i < 3) {
    
    
                    var left = $("#menuOnly0").offset().left;
                    $(".shadow-class").offset({
    
     left: left })
                } else if (i >= (this.dataArray.length - 3)) {
    
    
                    // 计算出 .shadow-class 盒子的位置
                    var left = $("#menuOnly" + (this.dataArray.length - 1)).offset().left - ($("#menuOnly0").outerWidth() * 4);
                    $(".shadow-class").offset({
    
     left: left });
                } else {
    
    
                    var left = $("#menuOnly" + i).offset().left - (($("#menuOnly0").outerWidth()) * 2);
                    $(".shadow-class").offset({
    
     left: left })
                }
            }, 200);
},

2.handleClickV2()方法-视频里调用到的此方法

移动阴影盒子,加了动画移动-此方法是先给小盒子变色,再移动阴影盒子

        handleClickV2(i) {
    
    
            setTimeout(() => {
    
    //需要用延对dom进行操作
                const menuItemWidth = $("#menuOnly0").outerWidth(); // 单个小盒子的宽度
                const shadowWidth = menuItemWidth * 5; // 大盒子的宽度为小盒子的五倍
                const shadow = $(".shadow-class"); // 大盒子元素
                const menuItem = $(`#menuOnly${
      
      i}`); // 点击的小盒子元素
                $(".shadow-class").width($("#menuOnly0").outerWidth() * 5);
                $("#menuOnly" + i).addClass("select").siblings().removeClass("select");
                if (i < 3) {
    
    
                    var left = 0;
                    shadow.animate({
    
     left }, 500);
                } else if (i >= (this.dataArray.length - 3)) {
    
    
                    // 若i为后三项,则直接定shadow-class以倒数第三项为中心
                    var left = Math.min(($(window).width() - shadowWidth, ($(`#menuOnly${
      
      this.dataArray.length - 3}`).offset().left - (menuItemWidth * 2))) - $("#menuOnly0").offset().left);
                    shadow.animate({
    
     left }, 500);
                } else {
    
    
                    var left = Math.min(($(window).width() - shadowWidth, (menuItem.offset().left - (menuItemWidth * 2))) - $("#menuOnly0").offset().left);
                    shadow.animate({
    
     left }, 500);
                }
            }, 200);
        },

3.handleClickV3()方法

移动阴影盒子,加了动画移动-此方法是先移动阴影盒子,操作结束后在给点击的小盒子变色

handleClickV3(i) {
    
    
            setTimeout(() => {
    
    //需要用延对dom进行操作
                const menuItemWidth = $("#menuOnly0").outerWidth(); // 单个小盒子的宽度
                const shadowWidth = menuItemWidth * 5; // 大盒子的宽度为小盒子的五倍
                const shadow = $(".shadow-class"); // 大盒子元素
                const menuItem = $(`#menuOnly${
      
      i}`); // 点击的小盒子元素
                $(".shadow-class").width($("#menuOnly0").outerWidth() * 5);
                if (i < 3) {
    
    
                    // 若i为前三项,则直接使阴影盒子的left定位在0的位置上
                    var left = 0;
                    shadow.animate({
    
    //这种方式小盒子背景显示会比较慢
                        left
                    }, {
    
    
                        duration: 500,
                        complete: function () {
    
    
                            $("#menuOnly" + i).addClass("select").siblings().removeClass("select");
                        }
                    });
                } else if (i >= (this.dataArray.length - 3)) {
    
    
                    // 若i为后三项,则直接定shadow-class以倒数第三项为中心
                    var left = Math.min(($(window).width() - shadowWidth, ($(`#menuOnly${
      
      this.dataArray.length - 3}`).offset().left - (menuItemWidth * 2))) - $("#menuOnly0").offset().left);
                    shadow.animate({
    
    
                        left
                    }, {
    
    
                        duration: 500,
                        complete: function () {
    
    
                            $("#menuOnly" + i).addClass("select").siblings().removeClass("select");
                        }
                    });
                } else {
    
    
                    var left = Math.min(($(window).width() - shadowWidth, (menuItem.offset().left - (menuItemWidth * 2))) - $("#menuOnly0").offset().left);
                    shadow.animate({
    
    
                        left
                    }, {
    
    
                        duration: 500,
                        complete: function () {
    
    
                            $("#menuOnly" + i).addClass("select").siblings().removeClass("select");
                        }
                    });
                }
            }, 200);
},

四、.offset() 和 .animate()的相似和区别

offset()animate() 是 jQuery 库中的两个方法。它们都可以用来处理 HTML 元素的位置和动画,但是有一些区别和相似之处。

相似处:
都可以用来改变元素的位置和动画效果。
都可以在元素上添加 CSS 样式或属性。
区别:
.offset() 方法用于获取或设置元素相对于文档的坐标位置,而 .animate() 方法则用于创建动画效果。
.offset() 方法只能改变元素的位置,而 .animate() 方法还可以改变元素的大小、透明度等其他属性。
.animate() 方法可以设置动画的效果持续时间和完成后的回调函数,而 .offset() 方法没有这样的选项。

总的来说,.offset() 方法更适合用于静态页面布局,而 .animate() 方法更适合用于动态交互性页面中的动画效果。

五、outerWidth()和width()的相似和区别

outerWidth()width() 都是用于获取元素宽度的 jQuery 方法

区别:
outerWidth() 方法返回包括元素的内边距和边框在内的整个宽度
width() 方法只返回元素的内容宽度。

总的来说,如果一个元素的 CSS 样式中设置了边框和内边距,那么计算出来的 outerWidth() 就会比 width() 大。下面是一个例子:

<div id="myElement" style="width: 200px; border: 2px solid black; padding: 10px;">
  This is some content.
</div>

使用 $(‘#myElement’).outerWidth() 将返回 224,因为这个元素的内容宽度是 200px,加上左右两侧各 2px 的边框和 10px 的内边距,总宽度就是 200 + 22 + 210 = 224。

使用 $(‘#myElement’).width() 将仅返回内容宽度 200,不包括边框和内边距。

相似处:
这两个方法都可以接受一个可选的布尔类型参数,表示是否包括 margin 在内,默认是不包括。如果将这个参数设置为 true,那么返回的宽度将包括 margin 的值。


全部代码

<!-- 若干个规则排列的小盒子点击时使其变色同时动画移动阴影大盒子跟随在其中间位置 -->
<template>
    <div class="big-class">
        <div class="bottom-class">
            <div class="shadow-class" id="pic"></div>
            <div class="outbox-class">
                <div v-for="(item,index) in dataArray" :key="index" :id="'menuOnly'+index" @click="selectOnlyFileV2(index,item)" class="box-class">
                    <div class="state-class">
                        <img v-bind:src="item.url" alt="" style="width: 85%; height: 85%">
                    </div>
                    <div class="stationnum-class">
                        {
    
    {
    
    item.name.slice(item.name.length-2,item.name.length)}}
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    
    
    components: {
    
    },
    name: "movedemo",
    data() {
    
    
        return {
    
    
            dataArray: [
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-01'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-02'
                },
                {
    
    
                    url: './images/excel.jpg',//不会显示
                    name: 'abcd-03'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-04'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-05'
                },
                {
    
    
                    url: require('./images/excel.jpg'),
                    name: 'abcd-06'
                },
                {
    
    
                    url: require('./images/excel.jpg'),
                    name: 'abcd-07'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-08'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-09'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-10'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-11'
                },
                {
    
    
                    url: require('./images/excel.jpg'),
                    name: 'abcd-12'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-13'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-14'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-15'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-16'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-17'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-18'
                },
                {
    
    
                    url: require('./images/excel.jpg'),
                    name: 'abcd-19'
                },
                {
    
    
                    url: require('./images/excel.jpg'),
                    name: 'abcd-20'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-21'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-22'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-23'
                },
                {
    
    
                    url: require('./images/popo.jpg'),
                    name: 'abcd-24'
                },
            ],
        };
    },
    props: {
    
    },
    computed: {
    
    },
    mounted() {
    
    
        this.selectOnlyFileV2(0, this.dataArray[0]);//默认初始为第一个
    },
    methods: {
    
    
        selectOnlyFileV2(i, item) {
    
    
            /**这里存操作代码 */
            // this.handleClickV1(i);
            this.handleClickV2(i);
            // this.handleClickV3(i);
        },
        // 点击并移动阴影盒子
        handleClickV1(i) {
    
    
            window.requestAnimationFrame(function () {
    
    //不需要等待异步执行完成--会导致与.shadow-class不同步,弃用
                $("#menuOnly" + i).addClass("select").siblings().removeClass("select");
            });
            setTimeout(() => {
    
    
                $(".shadow-class").width($("#menuOnly0").outerWidth() * 5);
                // $("#menuOnly" + i).addClass("select").siblings().removeClass("select");
                if (i < 3) {
    
    
                    var left = $("#menuOnly0").offset().left;
                    $(".shadow-class").offset({
    
     left: left })
                } else if (i >= (this.dataArray.length - 3)) {
    
    
                    // 计算出 .shadow-class 盒子的位置
                    var left = $("#menuOnly" + (this.dataArray.length - 1)).offset().left - ($("#menuOnly0").outerWidth() * 4);
                    $(".shadow-class").offset({
    
     left: left });
                } else {
    
    
                    var left = $("#menuOnly" + i).offset().left - (($("#menuOnly0").outerWidth()) * 2);
                    $(".shadow-class").offset({
    
     left: left })
                }
            }, 200);
        },
        // 移动阴影盒子,加了动画移动-此方法是先给小盒子变色,再移动阴影盒子
        handleClickV2(i) {
    
    
            setTimeout(() => {
    
    //需要用延对dom进行操作
                const menuItemWidth = $("#menuOnly0").outerWidth(); // 单个小盒子的宽度
                const shadowWidth = menuItemWidth * 5; // 大盒子的宽度为小盒子的五倍
                const shadow = $(".shadow-class"); // 大盒子元素
                const menuItem = $(`#menuOnly${
      
      i}`); // 点击的小盒子元素
                $(".shadow-class").width($("#menuOnly0").outerWidth() * 5);
                $("#menuOnly" + i).addClass("select").siblings().removeClass("select");
                if (i < 3) {
    
    
                    var left = 0;
                    shadow.animate({
    
     left }, 500);
                } else if (i >= (this.dataArray.length - 3)) {
    
    
                    // 若i为后三项,则直接定shadow-class以倒数第三项为中心
                    var left = Math.min(($(window).width() - shadowWidth, ($(`#menuOnly${
      
      this.dataArray.length - 3}`).offset().left - (menuItemWidth * 2))) - $("#menuOnly0").offset().left);
                    shadow.animate({
    
     left }, 500);
                } else {
    
    
                    var left = Math.min(($(window).width() - shadowWidth, (menuItem.offset().left - (menuItemWidth * 2))) - $("#menuOnly0").offset().left);
                    shadow.animate({
    
     left }, 500);
                }
            }, 200);
        },
        // 移动阴影盒子,加了动画移动-此方法是先移动阴影盒子,操作结束后在给点击的小盒子变色
        handleClickV3(i) {
    
    
            setTimeout(() => {
    
    //需要用延对dom进行操作
                const menuItemWidth = $("#menuOnly0").outerWidth(); // 单个小盒子的宽度
                const shadowWidth = menuItemWidth * 5; // 大盒子的宽度为小盒子的五倍
                const shadow = $(".shadow-class"); // 大盒子元素
                const menuItem = $(`#menuOnly${
      
      i}`); // 点击的小盒子元素
                $(".shadow-class").width($("#menuOnly0").outerWidth() * 5);
                if (i < 3) {
    
    
                    // 若i为前三项,则直接使阴影盒子的left定位在0的位置上
                    var left = 0;
                    shadow.animate({
    
    //这种方式小盒子背景显示会比较慢
                        left
                    }, {
    
    
                        duration: 500,
                        complete: function () {
    
    
                            $("#menuOnly" + i).addClass("select").siblings().removeClass("select");
                        }
                    });
                } else if (i >= (this.dataArray.length - 3)) {
    
    
                    // 若i为后三项,则直接定shadow-class以倒数第三项为中心
                    var left = Math.min(($(window).width() - shadowWidth, ($(`#menuOnly${
      
      this.dataArray.length - 3}`).offset().left - (menuItemWidth * 2))) - $("#menuOnly0").offset().left);
                    shadow.animate({
    
    
                        left
                    }, {
    
    
                        duration: 500,
                        complete: function () {
    
    
                            $("#menuOnly" + i).addClass("select").siblings().removeClass("select");
                        }
                    });
                } else {
    
    
                    var left = Math.min(($(window).width() - shadowWidth, (menuItem.offset().left - (menuItemWidth * 2))) - $("#menuOnly0").offset().left);
                    shadow.animate({
    
    
                        left
                    }, {
    
    
                        duration: 500,
                        complete: function () {
    
    
                            $("#menuOnly" + i).addClass("select").siblings().removeClass("select");
                        }
                    });
                }
            }, 200);
        },
    }
}
</script>
<style scoped>
.big-class{
    
    
    width: 100%;
    height: 100%;
    background: #ced5e3;
}
/* 底部缩略样式 */
.bottom-class {
    
    
    width: 100%;
    height: 10%;
    /* border: 1px solid red; */
    box-sizing: border-box;
    position: absolute;
    bottom: 15px;
    right: 0;
}
.outbox-class {
    
    
    height: 83%;
    display: flex;
    margin-top: 0.55%;
}
.box-class {
    
    
    flex: 1;
    border: 0.5px solid #32c4fd;
    /* border: 1px solid #a7a7a7; */
    z-index: 99;
    color: #fff;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
}
.box-class:hover {
    
    
    cursor: pointer;
    box-shadow: 0 0 25px 10px rgba(43, 177, 231, 0.2);
}
.shadow-class {
    
    
    /* width: 20%; */
    height: 100%;
    /* border: 1px solid yellow; */
    box-sizing: border-box;
    position: absolute;
    background: #3896d3;
    opacity: 0.2;
}
.shadow-class:hover {
    
    
    cursor: pointer;
}
.select {
    
    
    color: #2bb1e7;
    background: #346671;
}
</style>

猜你喜欢

转载自blog.csdn.net/weixin_42676530/article/details/130502864
今日推荐