shuisheng пре 2 година
родитељ
комит
ed18d58724
3 измењених фајлова са 298 додато и 0 уклоњено
  1. 176 0
      src/components/Slider/index.vue
  2. 113 0
      src/constant.ts
  3. 9 0
      src/views/home/index.vue

+ 176 - 0
src/components/Slider/index.vue

@@ -0,0 +1,176 @@
+<template>
+  <div class="slider-wrap">
+    <div
+      v-for="(slider, index) in sliderList"
+      :key="'slider-' + index"
+      class="slider"
+      :style="{
+        '--width': widthMap[index] / 2,
+        '--speed': widthMap[index] / 2 / speed + 's',
+      }"
+    >
+      <div
+        ref="containerRef"
+        class="container"
+      >
+        <div
+          v-for="(slide, indey) in slider"
+          :key="'slide-' + indey"
+          class="slide"
+        >
+          <div class="avatar">
+            <div
+              alt=""
+              class="img"
+              :style="{ backgroundImage: `url(${slide.img})` }"
+            ></div>
+          </div>
+
+          <span class="txt">
+            <span class="msg">{{ slide.txt }}</span>
+          </span>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { nextTick, onMounted, ref } from 'vue';
+
+const props = defineProps({
+  list: {
+    type: Array,
+    require: true,
+    default() {
+      return [];
+    },
+  },
+  // 多少行
+  row: {
+    type: Number,
+    default: 3,
+  },
+  // 滚动速率 (px/s)
+  speed: {
+    type: Number,
+    default: 100,
+  },
+});
+
+const containerRef = ref<HTMLDivElement[]>([]);
+const sliderList = ref<any[]>([]);
+const widthMap = ref<any>({});
+
+onMounted(() => {
+  const res: any[] = [];
+  const count = Math.ceil(props.list.length / props.row);
+  for (let i = 0, len = props.list.length; i < len; i += count) {
+    const item = props.list.slice(i, i + count);
+    res.push([...item, ...item]);
+  }
+  sliderList.value = res;
+  nextTick(() => {
+    const res = {};
+    containerRef.value.forEach((container, index) => {
+      for (let i = 0; i < container.children.length; i += 1) {
+        const slideList = container.children;
+        for (let y = 0; y < slideList.length; y += 1) {
+          const slide = slideList[y];
+          if (index === i) {
+            res[index] = res[index] || 0;
+            res[index] += slide.getBoundingClientRect().width;
+          }
+        }
+      }
+    });
+    widthMap.value = res;
+  });
+});
+</script>
+
+<style lang="scss" scoped>
+@keyframes left-right {
+  0% {
+    transform: translateX(calc(var(--width) * -1px));
+  }
+  100% {
+    transform: translateX(0);
+  }
+}
+
+@keyframes right-left {
+  0% {
+    transform: translateX(0);
+  }
+  100% {
+    transform: translateX(calc(var(--width) * -1px));
+  }
+}
+.slider-wrap {
+  .slider {
+    overflow-x: scroll;
+
+    @extend %hideScrollbar;
+
+    &:nth-child(2n) {
+      .container {
+        animation: left-right var(--speed) linear infinite;
+        &:hover {
+          animation-play-state: paused;
+        }
+      }
+    }
+    &:nth-child(2n-1) {
+      .container {
+        animation: right-left var(--speed) linear infinite;
+        &:hover {
+          animation-play-state: paused;
+        }
+      }
+    }
+
+    .container {
+      display: flex;
+
+      .slide {
+        display: flex;
+        align-items: center;
+        flex-shrink: 0;
+        box-sizing: border-box;
+        padding-right: 40px;
+        height: 40px;
+        .avatar {
+          display: flex;
+          overflow: hidden;
+          align-items: center;
+          justify-content: center;
+          margin-right: 10px;
+          width: 30px;
+          height: 30px;
+          border-radius: 50%;
+          background-color: #fff;
+
+          .img {
+            width: 30px;
+            height: 30px;
+
+            @extend %containBg;
+          }
+        }
+        .txt {
+          display: flex;
+          align-items: center;
+          font-size: 14px;
+          .msg {
+            display: inline-block;
+            max-width: 150px;
+
+            @extend %singleEllipsis;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 113 - 0
src/constant.ts

@@ -35,3 +35,116 @@ export const mediaTypeEnumMap = {
   [MediaTypeEnum.time]: '时间',
   [MediaTypeEnum.stopwatch]: '秒表',
 };
+
+export const sliderList = [
+  {
+    img: 'https://resource.hsslive.cn/billd-live/image/a4039f86e5352bcfccaddecc4b72a1df.webp',
+    txt: 'SRS',
+    link: 'https://ossrs.net',
+  },
+  {
+    img: 'https://resource.hsslive.cn/billd-live/image/1277df4371045310acbc4bf2fc0811b8.webp',
+    txt: 'Vue3',
+    link: 'https://vuejs.org',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/5ce36cab3d6b23974625900dc4cf39a3.webp',
+    txt: 'Node',
+    link: 'https://nodejs.org',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/dd907463af7fdec395e5f6d088b0308b.webp',
+    txt: 'Pinia',
+    link: 'https://pinia.vuejs.org',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/0214acde5f5f5e3caf278ce446cc4414.webp',
+    txt: 'WebRTC',
+    link: 'https://github.com/webrtc',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/d4417f70fa36edbc62b5aa3840cbf25f.webp',
+    txt: 'bilibili',
+    link: 'https://www.bilibili.com',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/a6473eed036e5d35ca2c9f7118c974cd.webp',
+    txt: 'Vite4',
+    link: 'https://vitejs.dev',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/627105f0e5674018cb03c8da036ae5d5.webp',
+    txt: 'Webpack5',
+    link: 'https://webpack.js.org',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/9d54ed9673f2ca4ffc78fc6348f2b736.png',
+    txt: 'TypeScript',
+    link: 'https://www.typescriptlang.org',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/0dcabc80c616240edc3111450fbf79aa.webp',
+    txt: 'socket.io',
+    link: 'https://socket.io',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/2009474c455813d487803e2acfcbb4af.webp',
+    txt: 'mysql',
+    link: 'https://www.mysql.com/',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/e66deaf779edb2a94e91f9b0f2995f6d.webp',
+    txt: 'redis',
+    link: 'https://redis.io',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/fd783552a400643c611c62e9200bb429.webp',
+    txt: 'Sequelize',
+    link: 'https://sequelize.org',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/d5eb237bd54bc4e729186115e89e5935.webp',
+    txt: 'Docker',
+    link: 'https://www.docker.com',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/c3c342f6852706e0b70d011e8753d2d6.webp',
+    txt: 'FFmpeg',
+    link: 'https://ffmpeg.org',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/dd8ffe33c22723381a3664684eaca237.png',
+    txt: 'esbuild',
+    link: 'https://esbuild.github.io',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/92a6f3e295634ddd21b6b8034fa3b25f.webp',
+    txt: 'Jenkins',
+    link: 'https://www.jenkins.io',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/dce3470845321ce654d09ce811837749.webp',
+    txt: '腾讯云云直播 CSS',
+    link: 'https://cloud.tencent.com/product/css',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/e247f6fd39320051d236f3f844b9056f.webp',
+    txt: '支付宝当面付',
+    link: 'https://opendocs.alipay.com/open/194',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/9a934ebf993f5d3b4146f050f7071518.webp',
+    txt: '七牛云对象存储',
+    link: 'https://www.qiniu.com',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/074835fbbaf976992e78bc6a585530e6.webp',
+    txt: '阿里云轻量服务器',
+    link: 'https://www.aliyun.com',
+  },
+  {
+    img: 'https://resource.hsslive.cn/image/354823b72eb805264c940f5232d824fe.webp',
+    txt: 'PM2',
+    link: 'https://github.com/Unitech/pm2',
+  },
+];

+ 9 - 0
src/views/home/index.vue

@@ -2,6 +2,13 @@
   <div class="home-wrap">
     <div class="play-container">
       <div class="bg"></div>
+      <div class="slider-wrap">
+        <Slider
+          v-if="interactionList.length"
+          :list="interactionList"
+          :row="2"
+        ></Slider>
+      </div>
       <div class="container">
         <div class="left">
           <div
@@ -180,6 +187,7 @@ import { onMounted, ref, watch } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 
 import { fetchLiveList } from '@/api/live';
+import { sliderList } from '@/constant';
 import { usePull } from '@/hooks/use-pull';
 import { ILive, LiveRoomTypeEnum, liveTypeEnum } from '@/interface';
 import { routerName } from '@/router';
@@ -192,6 +200,7 @@ const topNums = ref(6);
 const topLiveRoomList = ref<ILive[]>([]);
 const otherLiveRoomList = ref<ILive[]>([]);
 const currentLiveRoom = ref<ILive>();
+const interactionList = ref(sliderList);
 const localVideoRef = ref<HTMLVideoElement[]>([]);
 const remoteVideoRef = ref<HTMLDivElement>();