分享一个小东西,vue3+TypeScript的技术栈中,做一个点击切换模块展示状态(缓动)

82 阅读1分钟

先看样例图,一行占满三个元素,默认第一个占总宽的50%,点击谁,被点击的会缓动的放大挤压动画,去占据50%宽度

1.png 2.png

1、先看html代码

<div class="container" ref="container">
    <div class="rectangle" v-for="(item, index) in items" :key="index" :style="{ width: item.width, }" :class="{ clicked: item.clicked }" @click="toggleClick(index)">
      <div class="image" :class="{ small: !item.clicked, big: item.clicked }">
        <div class="image-font-f" v-if="item.clicked == false">
          <div class="textf1" style="color: #000;font-size: 2rem;">{{ item.title }}</div>
          <div class="textf2" style="color: #9FB2D2;margin-top: .66rem;letter-spacing: 1.5px;">{{ item.entitle }}</div>
        </div>
        <div class="image-font-t" v-if="item.clicked == true">
          <div class="textt1" style="color: #002864;font-size: 4.5rem;">{{ item.title }}</div>
          <div class="textt2" style="color: #A1A9BA;margin: 1.5rem 0;font-size: 1.5vw;letter-spacing: 2px;">{{ item.entitle }}</div>
          <div class="textt3" style="color: #002864;white-space: pre-wrap;font-size: 1.5rem;">{{ item.description }}</div>
          <div style="color: #7FA6D0;font-weight: 900;margin-top:1.7rem;">━━━</div>
        </div>
      </div>
    </div>
  </div>

2、script代码

<script lang="ts">
import { ref, onMounted, nextTick } from "vue";
import { onUnmounted } from "@vue/runtime-core";

export default {
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
  setup() {
    // 定义矩形元素的初始状态
    const items = ref([
      {
        width: "50%",
        marginLeft: "0px",
        clicked: true,
        title: "XX",
        entitle: "xxxx",
        description: "xxxxxxxxxxxxxxxx",
      },
      {
        width: "calc(50% / 2 - 5px)",
        marginLeft: "5px",
        clicked: false,
        title: "yy",
        entitle: "yyyyy",
        description: "yyyyyyyyyyyyyyyy",
      },
      {
        width: "calc(50% / 2 - 5px)",
        marginLeft: "5px",
        clicked: false,
        title: "zz",
        entitle: "zzzzzzzz",
        description: "zzzzzzzzzzzzzzzz",
      },
    ]);

    // 点击矩形元素时切换状态
    const toggleClick = (index: number) => {
      items.value.forEach((item, i) => {
        if (i === index) {
          item.clicked = true;
          item.width = "50%";
          item.marginLeft = "5px";
        } else {
          item.clicked = false;
          item.width = "calc(50% / 2 - 5px)";
          item.marginLeft = "5px";
        }
      });
    };

    // 在组件挂载时,调整矩形元素的宽度和间距,使其适应容器的宽度
    const adjustItems = () => {
      const container = document.querySelector(".container");
      if (!container) return;
      const containerWidth = container.clientWidth;
      const itemWidth = containerWidth / 2;
      const itemMargin = (containerWidth - itemWidth * 5) / 2;
      items.value.forEach(
        (item: { width: string; marginLeft: string }, index: number) => {
          item.width = index === 0 ? `${itemWidth}px` : `${itemWidth / 2}px`;
          item.marginLeft = index === 0 ? "0px" : `${itemMargin}px`;
        }
      );
    };

    // 在组件挂载后和窗口大小变化时,调整矩形元素的宽度和间距,使其适应容器的宽度
    onMounted(() => {
      nextTick(() => {
        adjustItems();
      });
      window.addEventListener("resize", adjustItems);
    });

    // 在组件卸载时,移除窗口大小变化事件的监听器
    onUnmounted(() => {
      window.removeEventListener("resize", adjustItems);
    });

    return { items, toggleClick };
  },
};
</script>

3、css代码

<style lang="scss" scoped>
.container {
  width: 100%;
  display: flex;
  justify-content: space-evenly;
  height: 35rem;
  margin: 0 auto;
}

.rectangle {
  position: relative;
  height: 100%;
  margin-right: 10px;
  background-color: #ccc;
  display: flex;
  justify-content: center;
  align-items: center;
  transition: width 0.5s ease-in-out;
  cursor: pointer;
  border-radius: 10px;
  // box-shadow: 0 0 6px 1px rgba(0, 0, 0, 0.2);
  box-shadow: 0px 8px 20px 0px rgba(55, 99, 170, 0.2);
}

.rectangle:first-child {
  width: 50%;
}

.rectangle:not(:first-child) {
  width: calc(50% / 4 - 5px);
}

.rectangle.clicked {
  width: 50%;
  margin-left: 5px;
  // border-radius: 10px;
}

.image {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-size: contain;
  background-repeat: no-repeat;

  .image-font-f {
    position: relative;
    margin-top: 7rem;
    margin-left: 2rem;
    font-weight: 600;
  }
  .image-font-t {
    position: relative;
    margin-top: 7rem;
    margin-left: 2rem;
    font-weight: 600;
  }
}

.image.small {
  background-image: url("../../../public/img/solution/home_one.png");
  background-size: 105% 103%;
  background-position: -0.38rem -0.33rem;
  background-color: #fff;
  // border-radius: 10px;
}

.image.big {
  background-image: url("../../../public/img/solution/home_one_bg.png");
  background-color: #fff;
  background-repeat: no-repeat;
  background-size: 102% 103%;
  background-position: -0.33rem -0.33rem;
  // border-radius: 10px;
}

.rectangle:nth-child(2) .image.small {
  background-image: url("../../../public/img/solution/home_two.png");
  background-size: 105% 103%;
  background-position: -0.38rem -0.33rem;
  background-color: #fff;
  // border-radius: 10px;
}

.rectangle:nth-child(2) .image.big {
  background-image: url("../../../public/img/solution/home_two_bg.png");
  background-color: #fff;
  background-repeat: no-repeat;
  background-size: 102% 103%;
  background-position: -0.33rem -0.33rem;
  // border-radius: 10px;
}

.rectangle:nth-child(3) .image.small {
  background-image: url("../../../public/img/solution/home_three.png");
  background-size: 105% 103%;
  background-position: -0.38rem -0.33rem;
  background-color: #fff;
  // border-radius: 10px;
}

.rectangle:nth-child(3) .image.big {
  background-image: url("../../../public/img/solution/home_three_bg.png");
  background-color: #fff;
  background-repeat: no-repeat;
  background-size: 102% 103%;
  background-position: -0.33rem -0.33rem;
  // border-radius: 10px;
}
</style>

以上就是实现这个效果的全部代码了,有需要做这个小东西的话,直接施展CV大法即可