vue实现Slider Card 组件

113 阅读7分钟

下面是一个利用css和input的一些小技巧,所实现的一个比较炫酷和美观的card卡片 当点击下面的slider时会实现内容的切换 当点击左侧的图片时,图片会铺满这个内容区域,同时内容隐藏,当再次点击放大后的图片时便恢复原状

效果

未命名.gif

思路

主要就是对css样式的利用以及对input标签的巧妙应用

原文地址

codepen.io/batuhanbas/… 这是一个提供一些特效的网站,很不错喔~~~

原生方式实现

<div class="card">
  <input type="radio" name="select" id="slide_1" checked>
  <input type="radio" name="select" id="slide_2">
  <input type="radio" name="select" id="slide_3">
  <input type="checkbox" id="slideImg">

  <div class="slider">
    <label for="slide_1" class="slide slide_1"></label>
    <label for="slide_2" class="slide slide_2"></label>
    <label for="slide_3" class="slide slide_3"></label>
  </div>

  <div class="inner_part">
    <label for="slideImg" class="img">
      <img class="img_1" src="https://c4.wallpaperflare.com/wallpaper/978/131/617/kiz-kulesi-turkey-istanbul-maiden-s-tower-wallpaper-preview.jpg">
    </label>
    <div class="content content_1">
      <div class="title">İstanbul</div>
      <div class="text">
        Istanbul, a fascinating city built on two Continents, divided by the Bosphorus Strait. This is one of the greatest cities in the world.
      </div>
      <button>Read More</button>
    </div>
  </div>

  <div class="inner_part">
    <label for="slideImg" class="img">
      <img class="img_2" src="https://c4.wallpaperflare.com/wallpaper/649/96/56/ankara-cityscape-night-night-sky-wallpaper-preview.jpg">
    </label>
    <div class="content content_2">
      <div class="title">Ankara</div>
      <div class="text">
        Ankara is Turkey's beating heart, second largest city, located in the Central Anatolia region and home to the Grand National Assembly of Turkey.
      </div>
      <button>Read More</button>
    </div>
  </div>

  <div class="inner_part">
    <label for="slideImg" class="img">
      <img class="img_3" src="https://c4.wallpaperflare.com/wallpaper/620/34/558/turkey-izmir-mountains-wallpaper-preview.jpg">
    </label>
    <div class="content content_3">
      <div class="title">İzmir</div>
      <div class="text">Located on the shores of the Aegean Sea, west of the Anatolian Peninsula, İzmir is the third-largest city in Turkey.
      </div>
      <button>Read More</button>
    </div>
  </div>
</div>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html, body {
  height: 100vh;
  font-family: 'Open Sans', sans-serif;
  background-color: #666666;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
}

.card {
  position: absolute;
  height: 350px;
  width: 100%;
  max-width: 850px;
  margin: auto;
  background-color: #ffffff;
  border-radius: 25px;
  box-shadow: 10px 0 50px rgba(0, 0, 0, 0.5);
}

.card .inner_part {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0 0 30px;
  height: 350px;
  position: absolute;
}

#slideImg:checked ~ .inner_part {
  padding: 0;
  transition: .1s ease-in;
}

.inner_part .img {
  height: 260px;
  width: 260px;
  overflow: hidden;
  flex-shrink: 0;
  border-radius: 20px;
  box-shadow: 0 10px 50px rgba(0,0,0,0.2);
}

#slideImg:checked ~ .inner_part .img {
 height: 350px;
 width: 850px;
 z-index: 99;
 transition: .3s .2s ease-in;
}

.img img {
  height: 100%;
  width: 100%;
  cursor: pointer;
  opacity: 0;
  transition: .6s;
}

#slide_1:checked ~ .inner_part .img_1,
#slide_2:checked ~ .inner_part .img_2,
#slide_3:checked ~ .inner_part .img_3 {
  opacity: 1;
  transition-delay: .2s;
}

.content {
  padding: 0 20px 0 35px;
  width: 530px;
  margin-left: 50px;
  opacity: 0;
  transition: .6s; 
}

#slideImg:checked ~ .inner_part .content {
  display: none;
}

#slide_1:checked ~ .inner_part .content_1,
#slide_2:checked ~ .inner_part .content_2,
#slide_3:checked ~ .inner_part .content_3 {
  opacity: 1;
  margin-left: 0;
  z-index: 100;
  transition-delay: .3s;
}

.content .title {
  font-size: 30px;
  font-weight: 700;
  color: #0d0925;
  margin: 0 0 20px 0;
}

.content .text {
  font-size: 19px;
  color: #4e4a67;
  margin: 0 auto 30px auto;
  line-height: 1.5em;
  text-align: justify;
}

.content button {
  padding: 15px 20px;
  border: none;
  font-size: 16px;
  color: #fff0e6;
  font-weight: 600;
  letter-spacing: 1px;
  border-radius: 50px;
  cursor: pointer;
  outline: none;
  background: #000000;
  float: right;
}

.content button:hover {
  background: #cecece;
  color: #000000;
}

.slider {
  position: absolute;
  bottom: 25px;
  left: 55%;
  transform: translateX(-50%);
  z-index: 1;
}

#slideImg:checked ~ .slider {
  display: none;
}

.slider .slide {
  position: relative;
  height: 10px;
  width: 50px;
  background: #d9d9d9;
  border-radius: 5px;
  display: inline-flex;
  margin: 0 3px;
  cursor: pointer;
}


.slider .slide:before {
  position: absolute;
  content: '';
  top: 0;
  left: 0;
  height: 100%;
  width: -100%;
  background: #000000;;
  border-radius: 10px;
  transform: scaleX(0);
  transition: transform .6s;
  transform-origin: left;
}

#slide_1:checked ~ .slider .slide_1:before,
#slide_2:checked ~ .slider .slide_2:before,
#slide_3:checked ~ .slider .slide_3:before {
  transform: scaleX(1);
  width: 100%;
}

input {
  display: none;
}

小编在其原有的基础之上利用vue对其加以实现,具体代码如下

利用vue实现

版本一

<template>
  <div class="card">
    <!-- 关联指示点 -->
    <input
      type="radio"
      name="select"
      id="slide_1"
      v-model="selectedSlide"
      value="1"
    />
    <input
      type="radio"
      name="select"
      id="slide_2"
      v-model="selectedSlide"
      value="2"
    />
    <input
      type="radio"
      name="select"
      id="slide_3"
      v-model="selectedSlide"
      value="3"
    />

    <!-- 图片全屏 -->
    <input type="checkbox" id="slideImg" />

    <!-- 指示点 -->
    <div class="slider">
      <label for="slide_1" class="slide slide_1"></label>
      <label for="slide_2" class="slide slide_2"></label>
      <label for="slide_3" class="slide slide_3"></label>
    </div>

    <!-- 内容区域 -->
    <div class="inner_part" :class="'inner_part_' + selectedSlide">
      <label for="slideImg" class="img">
        <img :src="selectedImage" :class="'img_' + selectedSlide" />
      </label>
      <div class="content" :class="'content_' + selectedSlide">
        <div class="title">{{ selectedTitle }}</div>
        <div class="text">{{ selectedText }}</div>
        <button>Read More</button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      selectedSlide: '1',
    }
  },
  computed: {
    selectedImage() {
      if (this.selectedSlide === '1') {
        return `https://c4.wallpaperflare.com/wallpaper/978/131/617/kiz-kulesi-turkey-istanbul-maiden-s-tower-wallpaper-preview.jpg`
      } else if (this.selectedSlide === '2') {
        return `https://c4.wallpaperflare.com/wallpaper/649/96/56/ankara-cityscape-night-night-sky-wallpaper-preview.jpg`
      } else if (this.selectedSlide === '3') {
        return `https://c4.wallpaperflare.com/wallpaper/620/34/558/turkey-izmir-mountains-wallpaper-preview.jpg`
      }
    },
    selectedTitle() {
      if (this.selectedSlide === '1') {
        return 'İstanbul'
      } else if (this.selectedSlide === '2') {
        return 'Ankara'
      } else if (this.selectedSlide === '3') {
        return 'İzmir'
      }
    },
    selectedText() {
      if (this.selectedSlide === '1') {
        return 'Istanbul, a fascinating city built on two Continents, divided by the Bosphorus Strait. This is one of the greatest cities in the world.'
      } else if (this.selectedSlide === '2') {
        return "Ankara is Turkey's beating heart, second largest city, located in the Central Anatolia region and home to the Grand National Assembly of Turkey."
      } else if (this.selectedSlide === '3') {
        return 'Located on the shores of the Aegean Sea, west of the Anatolian Peninsula, İzmir is the third-largest city in Turkey.'
      }
    },
  },
  watch: {
    selectedSlide(newVal, oldVal) {
      // Handle the slide change event here
    },
  },
}
</script>

<style lang="scss" scoped>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* 增加以下样式以控制内容的显示和隐藏 */
.inner_part_1 .content_1,
.inner_part_2 .content_2,
.inner_part_3 .content_3 {
  display: block;
}

.inner_part_1 .img_1,
.inner_part_2 .img_2,
.inner_part_3 .img_3 {
  display: block;
}

.inner_part_1 .content_2,
.inner_part_1 .content_3,
.inner_part_2 .content_1,
.inner_part_2 .content_3,
.inner_part_3 .content_1,
.inner_part_3 .content_2 {
  display: none;
}

.inner_part_1 .img_2,
.inner_part_1 .img_3,
.inner_part_2 .img_1,
.inner_part_2 .img_3,
.inner_part_3 .img_1,
.inner_part_3 .img_2 {
  display: none;
}

.card {
  position: absolute;
  height: 350px;
  width: 100%;
  max-width: 850px;
  margin: auto;
  background-color: #ffffff;
  border-radius: 25px;
  box-shadow: 10px 0 50px rgba(0, 0, 0, 0.5);
  font-family: 'Open Sans', sans-serif;
}

.card .inner_part {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0 0 30px;
  height: 350px;
  position: absolute;
}

#slideImg:checked ~ .inner_part {
  padding: 0;
  transition: 0.1s ease-in;
}

.inner_part .img {
  height: 260px;
  width: 260px;
  overflow: hidden;
  flex-shrink: 0;
  border-radius: 20px;
  box-shadow: 0 10px 50px rgba(0, 0, 0, 0.2);
}

#slideImg:checked ~ .inner_part .img {
  height: 350px;
  width: 850px;
  z-index: 99;
  transition: 0.3s 0.2s ease-in;
}

.img img {
  height: 100%;
  width: 100%;
  cursor: pointer;
  opacity: 0;
  transition: 0.6s;
}

#slide_1:checked ~ .inner_part .img_1,
#slide_2:checked ~ .inner_part .img_2,
#slide_3:checked ~ .inner_part .img_3 {
  opacity: 1;
  transition-delay: 0.2s;
}

.content {
  padding: 0 20px 0 35px;
  width: 530px;
  margin-left: 50px;
  opacity: 0;
  transition: 0.6s;
}

#slideImg:checked ~ .inner_part .content {
  display: none;
}

#slide_1:checked ~ .inner_part .content_1,
#slide_2:checked ~ .inner_part .content_2,
#slide_3:checked ~ .inner_part .content_3 {
  opacity: 1;
  margin-left: 0;
  z-index: 100;
  transition-delay: 0.3s;
}

.content .title {
  font-size: 30px;
  font-weight: 700;
  color: #0d0925;
  margin: 0 0 20px 0;
}

.content .text {
  font-size: 19px;
  color: #4e4a67;
  margin: 0 auto 30px auto;
  line-height: 1.5em;
  text-align: justify;
}

.content button {
  padding: 15px 20px;
  border: none;
  font-size: 16px;
  color: #fff0e6;
  font-weight: 600;
  letter-spacing: 1px;
  border-radius: 50px;
  cursor: pointer;
  outline: none;
  background: #000000;
  float: right;
}

.content button:hover {
  background: #cecece;
  color: #000000;
}

.slider {
  position: absolute;
  bottom: 25px;
  left: 55%;
  transform: translateX(-50%);
  z-index: 1;
}

#slideImg:checked ~ .slider {
  display: none;
}

.slider .slide {
  position: relative;
  height: 10px;
  width: 50px;
  background: #d9d9d9;
  border-radius: 5px;
  display: inline-flex;
  margin: 0 3px;
  cursor: pointer;
}

.slider .slide:before {
  position: absolute;
  content: '';
  top: 0;
  left: 0;
  height: 100%;
  width: -100%;
  background: #000000;
  border-radius: 10px;
  transform: scaleX(0);
  transition: transform 0.6s;
  transform-origin: left;
}

#slide_1:checked ~ .slider .slide_1:before,
#slide_2:checked ~ .slider .slide_2:before,
#slide_3:checked ~ .slider .slide_3:before {
  transform: scaleX(1);
  width: 100%;
}

input {
  display: none;
}
</style>

版本二

<template>
  <div class="card">
    <!-- 关联指示点 -->
    <input
      type="radio"
      name="select"
      :id="'slide_' + slide.id"
      v-model="selectedSlide"
      :value="slide.id"
      v-for="slide in slides"
      :key="slide.id"
    />

    <!-- 图片全屏 -->
    <input type="checkbox" id="slideImg" />

    <!-- 指示点 -->
    <div class="slider">
      <label
        :for="'slide_' + slide.id"
        :class="[
          'slide',
          'slide_' + slide.id,
          { 'label-checked': slide.id === selectedSlide },
        ]"
        v-for="slide in slides"
        :key="slide.id"
      ></label>
    </div>

    <!-- 内容区域 -->
    <div class="inner_part" :class="'inner_part_' + selectedSlide">
      <label for="slideImg" class="img">
        <img
          :src="selectedSlideData.image"
          :class="'img img_' + selectedSlide"
        />
      </label>
      <div class="content" :class="'content content_' + selectedSlide">
        <div class="title">{{ selectedSlideData.title }}</div>
        <div class="text">{{ selectedSlideData.text }}</div>
        <button>Read More</button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      selectedSlide: '1',
      slides: [
        {
          id: '1',
          title: 'İstanbul',
          text: 'Istanbul, a fascinating city built on two Continents, divided by the Bosphorus Strait. This is one of the greatest cities in the world.',
          image:
            'https://c4.wallpaperflare.com/wallpaper/978/131/617/kiz-kulesi-turkey-istanbul-maiden-s-tower-wallpaper-preview.jpg',
        },
        {
          id: '2',
          title: 'Ankara',
          text: "Ankara is Turkey's beating heart, second largest city, located in the Central Anatolia region and home to the Grand National Assembly of Turkey.",
          image:
            'https://c4.wallpaperflare.com/wallpaper/649/96/56/ankara-cityscape-night-night-sky-wallpaper-preview.jpg',
        },
        {
          id: '3',
          title: 'İzmir',
          text: 'Located on the shores of the Aegean Sea, west of the Anatolian Peninsula, İzmir is the third-largest city in Turkey.',
          image:
            'https://c4.wallpaperflare.com/wallpaper/620/34/558/turkey-izmir-mountains-wallpaper-preview.jpg',
        },
      ],
    }
  },
  computed: {
    selectedSlideData() {
      console.log(this.slides.find((slide) => slide.id === this.selectedSlide))
      return this.slides.find((slide) => slide.id === this.selectedSlide)
    },
  },
  watch: {
    selectedSlide(newVal, oldVal) {
      // console.log(newVal)
      // Handle the slide change event here
    },
  },
}
</script>

<style scoped>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.card {
  position: absolute;
  height: 350px;
  width: 100%;
  max-width: 850px;
  margin: auto;
  background-color: #ffffff;
  border-radius: 25px;
  box-shadow: 10px 0 50px rgba(0, 0, 0, 0.5);
  font-family: 'Open Sans', sans-serif;

  overflow: hidden;
}

.card .inner_part {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0 0 30px;
  height: 350px;
  position: absolute;
}

#slideImg:checked ~ .inner_part {
  padding: 0;
  transition: 0.1s ease-in;
}

.inner_part .img {
  height: 260px;
  width: 260px;
  overflow: hidden;
  flex-shrink: 0;
  border-radius: 20px;
  box-shadow: 0 10px 50px rgba(0, 0, 0, 0.2);
}

#slideImg:checked ~ .inner_part .img {
  height: 350px;
  width: 850px;
  z-index: 99;
  transition: 0.3s 0.2s ease-in;
}

.img img {
  height: 100%;
  width: 100%;
  cursor: pointer;
  /* opacity: 0; */
  transition: 0.6s;
}

.label-checked + .inner_part .img {
  opacity: 1;
  transition-delay: 0.2s;
}

.content {
  padding: 0 20px 0 35px;
  width: 530px;
  /* opacity: 0; */
  transition: 0.6s;
}

.label-checked + .inner_part .content {
  display: block;
}

.content + .title {
  opacity: 1;
  margin-left: 0;
  z-index: 100;
  transition-delay: 0.3s;
}

.content .title {
  font-size: 30px;
  font-weight: 700;
  color: #0d0925;
  margin: 0 0 20px 0;
}

.content .text {
  font-size: 19px;
  color: #4e4a67;
  margin: 0 auto 30px auto;
  line-height: 1.5em;
  text-align: justify;
}

.content button {
  padding: 15px 20px;
  border: none;
  font-size: 16px;
  color: #fff0e6;
  font-weight: 600;
  letter-spacing: 1px;
  border-radius: 50px;
  cursor: pointer;
  outline: none;
  background: #000000;
  float: right;
}

.content button:hover {
  background: #cecece;
  color: #000000;
}

.slider {
  position: absolute;
  bottom: 25px;
  left: 55%;
  transform: translateX(-50%);
  z-index: 1;
  display: flex;
}

.slider .slide {
  position: relative;
  height: 10px;
  width: 50px;
  background: #d9d9d9;
  border-radius: 5px;
  display: inline-flex;
  margin: 0 3px;
  cursor: pointer;
}

.slider .slide:before {
  position: absolute;
  content: '';
  top: 0;
  left: 0;
  height: 100%;
  width: -100%;
  background: #000000;
  border-radius: 10px;
  transform: scaleX(0);
  transition: transform 0.6s;
  transform-origin: left;
}

#slide_1:checked ~ .slider .slide_1:before,
#slide_2:checked ~ .slider .slide_2:before,
#slide_3:checked ~ .slider .slide_3:before {
  transform: scaleX(1);
  width: 100%;
}

input {
  display: none;
}
</style>