vue实现卡片遮罩层交互式功能

前言

在前端开发中,卡片遮罩层是一种常见的交互设计元素,用于强调某个区域或内容,并提供用户操作的入口。本文将带大家在 vue 中结合实际案例实现此功能。


实现效果


完整代码

html

html 复制代码
<template>
  <!-- 主容器 -->
  <div class="box">
    <div
      class="card"
      @click="cardDetails(index)"
      @mousedown.prevent="startLongPress($event, index)"
      @mouseup="stopLongPress"
      @mouseleave="stopLongPress"
      v-for="(item, index) in list"
      :key="index"
      ref="cardRef"
    >
      <!-- 卡片内容 -->
      <div class="content">
        <div class="cardImg">
          <img :src="item.imgUrl" alt="" />
        </div>
        <div class="introduce">{{ item.name }}</div>
      </div>
      <!-- 长按时显示的遮罩层 -->
      <div
        v-show="item.showOverlay"
        class="overlay"
        :class="{ 'expand-animation': item.showOverlay }"
        @click.stop
      >
        <div class="buttonCon">
          <div
            v-for="(shadeItem, shadeIndex) in shadeList"
            :key="shadeIndex"
            @click="onShade(shadeItem.title)"
          >
            {{ shadeItem.title }}
          </div>
        </div>
        <span class="close" @click="closeOverlay($event, index)">&times;</span>
      </div>
    </div>
  </div>
</template>

js

js 复制代码
<script>
export default {
  data() {
    return {
      shadeList: [
        { title: "商品不感兴趣" },
        { title: "不想看到此类商品" },
        { title: "已经买了" },
        { title: "图片引起不适" },
        { title: "更多..." },
      ],
      longPressTimer: null, // 用于存储长按计时器
      list: [
        {
          imgUrl: "https://img01.yzcdn.cn/vant/cat.jpeg",
          name: "xxxxxxxxxxxxxxxxxxxxxxx",
          showOverlay: false,
        },
        {
          imgUrl: "https://img01.yzcdn.cn/vant/cat.jpeg",
          name: "xxxxxxxxxxxxxxxxxxxxxxx",
          showOverlay: false,
        },
        {
          imgUrl: "https://img01.yzcdn.cn/vant/cat.jpeg",
          name: "xxxxxxxxxxxxxxxxxxxxxxx",
          showOverlay: false,
        },
        {
          imgUrl: "https://img01.yzcdn.cn/vant/cat.jpeg",
          name: "xxxxxxxxxxxxxxxxxxxxxxx",
          showOverlay: false,
        },
        {
          imgUrl: "https://img01.yzcdn.cn/vant/cat.jpeg",
          name: "xxxxxxxxxxxxxxxxxxxxxxx",
          showOverlay: false,
        },
        {
          imgUrl: "https://img01.yzcdn.cn/vant/cat.jpeg",
          name: "xxxxxxxxxxxxxxxxxxxxxxx",
          showOverlay: false,
        },
        {
          imgUrl: "https://img01.yzcdn.cn/vant/cat.jpeg",
          name: "xxxxxxxxxxxxxxxxxxxxxxx",
          showOverlay: false,
        },
        {
          imgUrl: "https://img01.yzcdn.cn/vant/cat.jpeg",
          name: "xxxxxxxxxxxxxxxxxxxxxxx",
          showOverlay: false,
        },
      ],
    };
  },
  mounted() {
    // 监听窗口滚动
    window.addEventListener("scroll", this.closeAllOverlaysOnScroll);
  },
  // 实例销毁前移除监听窗口的滚动
  beforeDestroy() {
    window.removeEventListener("scroll", this.closeAllOverlaysOnScroll);
  },
  methods: {
    // 滚动时关闭遮罩层
    closeAllOverlaysOnScroll() {
      this.list.forEach((item) => {
        this.$set(item, "showOverlay", false);
      });
    },
    // 开始长按事件
    startLongPress(event, index) {
      event.preventDefault();
      event.stopPropagation();
      this.closeOtherOverlays(index);
      this.longPressTimer = setTimeout(() => {
        this.list[index].showOverlay = true;
        document.addEventListener("click", this.checkClickOutside);
      }, 100);
    },
    closeOtherOverlays(index) {
      this.list.forEach((item, i) => {
        if (i !== index) {
          item.showOverlay = false;
        }
      });
    },
    // 点击卡片详情
    cardDetails(index) {
      if (!this.list[index].showOverlay) {
        console.log("点击卡片");
      }
    },
    // 结束长按事件
    stopLongPress() {
      clearTimeout(this.longPressTimer);
      document.removeEventListener("click", this.checkClickOutside);
    },
    // 关闭遮罩层
    closeOverlay(event, index) {
      event.stopPropagation(); // 阻止事件冒泡
      this.$set(this.list[index], "showOverlay", false); // 关闭当前卡片的遮罩层
      document.removeEventListener("click", this.checkClickOutside);
    },
    // 检查点击区域是否在遮罩层外
    checkClickOutside(event) {
      // 遍历所有卡片,关闭非点击卡片的遮罩层
      this.list.forEach((item, index) => {
        if (!this.$refs.cardRef[index].contains(event.target)) {
          this.$set(item, "showOverlay", false);
        }
      });
      document.removeEventListener("click", this.checkClickOutside);
    },
    // 点击遮罩层内容
    onShade(name) {
      console.log(name);
    },
  },
};
</script>

css

css 复制代码
<style scoped lang="less">
.box {
  font-size: 16px;
  min-height: 100vh;
  background: #f0f0f0;
  padding: 8px;
  .card {
    width: 49%;
    display: inline-block;
    background-color: white;
    position: relative;
    overflow-wrap: break-word;
    user-select: none;
    margin-bottom: 10px;
    border-radius: 8px;
    .introduce {
      text-align: justify;
      overflow: hidden;
      padding: 0px 6px 6px 6px;
      font-size: 14px;
      height: 50px;
      font-size: 14px;
    }
    .cardImg {
      img {
        border-top-left-radius: 8px;
        border-top-right-radius: 8px;
        width: 100%;
        height: 120px;
      }
    }
    .overlay {
      padding: 16px 10px;
      box-sizing: border-box;
      border-radius: 8px;
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background-color: rgba(35, 35, 35, 0.8);
      &.expand-animation {
        animation: expand 0.3s forwards;
      }
    }

    .buttonCon {
      font-size: 14px;
      width: 100%;
      color: rgb(213, 207, 207);
      div:not(:last-child) {
        border-bottom: 1px solid rgb(82, 82, 82);
        margin-bottom: 8px;
      }
      div {
        padding: 0px 2px 4px 2px;
      }
    }

    .close {
      position: absolute;
      top: 2px;
      right: 8px;
      font-size: 20px;
      cursor: pointer;
      color: white;
    }
  }
  .card:nth-child(even) {
    margin-left: 2%;
  }
}

@keyframes expand {
  0% {
    transform: scale(0);
    opacity: 0;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}
</style>

实现思路

这段代码实现了一个交互式的卡片列表功能,其中每个卡片包含一个图片和一些文字介绍。用户可以通过点击卡片来查看详情,同时也可以通过长按卡片来显示一个遮罩层,遮罩层上有一些操作按钮,如"商品不感兴趣"、"不想看到此类商品"等选项。此外,用户还可以点击遮罩层外的区域来关闭遮罩层。整体功能类似于一个商品展示页面,用户可以对商品进行不同的操作和反馈。

首先,实现卡片列表展示功能。通过循环遍历一个包含图片和文字介绍的数据数组,动态生成多个卡片组件。每个卡片组件包含一个图片元素和一个文字介绍元素,通过 vuev-for 指令实现数据绑定,将对应的图片和文字展示在每个卡片上。

其次,实现点击卡片查看详情的功能。为每个卡片组件添加点击事件监听器,当用户点击某个卡片时,触发相应的事件处理函数,展示该卡片的详细信息。这可以通过 vue@click 指令实现,为每个卡片元素添加点击事件处理逻辑。

接着,实现长按卡片显示遮罩层的功能。在代码中,为每个卡片组件添加长按事件监听器,当用户长按某个卡片时,显示遮罩层。通过 vue@touchstart 指令监听长按事件,并在事件触发时显示遮罩层。遮罩层可以是一个覆盖在卡片上方的半透明层,用来提供操作按钮和用户反馈选项。

在遮罩层上添加操作按钮,如"商品不感兴趣"、"不想看到此类商品"等选项。用户可以通过点击这些按钮来进行不同的操作和反馈。通过在遮罩层组件中添加按钮元素,并为每个按钮添加点击事件处理逻辑来实现。

最后,实现点击遮罩层外的区域关闭遮罩层的功能。为遮罩层外的区域添加点击事件监听器,当用户点击遮罩层外的区域时,关闭遮罩层。通过 vue@click 指令监听遮罩层外区域的点击事件,并在事件触发时关闭遮罩层。

相关推荐
轻口味1 小时前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王1 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发1 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀2 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
yg_小小程序员3 小时前
vue3中使用vuedraggable实现拖拽
typescript·vue
ekskef_sef4 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6414 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻5 小时前
Vue(四)
前端·javascript·vue.js