Commit 32cb530ce9556f0ade4ef7d06d8b74924e771205

Authored by fengwotao
1 parent 086c4b50

perf(external/Composes): 修改摄像头排布

1 1 <template>
2   - <div class="go-content-box" :style="{ width: w + 'px', height: h + 'px' }">
3   - <video
  2 + <video
4 3 crossOrigin="anonymous"
5   - :id="`my-player${index}`" ref="videoRef" class="video-js my-video vjs-theme-city vjs-big-play-centered">
6   - <source :src="sourceSrc" />
7   - </video>
8   - </div>
  4 + :id="`my-player${index}`"
  5 + ref="videoRef"
  6 + class="video-js my-video vjs-theme-city vjs-big-play-centered"
  7 + >
  8 + <source :src="sourceSrc" />
  9 + </video>
9 10 </template>
10 11 <script setup lang="ts">
11 12 import { onMounted, ref, onUnmounted, watch } from 'vue'
... ... @@ -97,14 +98,8 @@ defineExpose({
97 98 </script>
98 99
99 100 <style lang="scss" scoped>
100   -.go-content-box {
101   - display: flex;
102   - align-items: center;
103   - justify-content: center;
104   - .my-video {
105   - width: 100% !important;
106   - height: 100% !important;
107   - position: relative;
108   - }
  101 +.my-video {
  102 + width: 100%;
  103 + height: 100%;
109 104 }
110 105 </style>
... ...
  1 +<template>
  2 + <div class="go-content-box" :style="{ width: w + 'px', height: h + 'px' }">
  3 + <video
  4 + crossOrigin="anonymous"
  5 + :id="`my-player${index}`" ref="videoRef" class="video-js my-video vjs-theme-city vjs-big-play-centered">
  6 + <source :src="sourceSrc" />
  7 + </video>
  8 + </div>
  9 +</template>
  10 +<script setup lang="ts">
  11 +import { onMounted, ref, onUnmounted, watch } from 'vue'
  12 +import videojs from 'video.js'
  13 +import type { VideoJsPlayerOptions } from 'video.js'
  14 +import 'video.js/dist/video-js.min.css'
  15 +
  16 +const props = defineProps({
  17 + sourceSrc: {
  18 + type: String
  19 + },
  20 + name: {
  21 + type: String
  22 + },
  23 + avatar: {
  24 + type: String
  25 + },
  26 + w: {
  27 + type: Number,
  28 + default: 300
  29 + },
  30 + h: {
  31 + type: Number,
  32 + default: 300
  33 + },
  34 + index: {
  35 + type: Number
  36 + }
  37 +})
  38 +
  39 +// video标签
  40 +const videoRef = ref<HTMLElement | null>(null)
  41 +
  42 +// video实例对象
  43 +let videoPlayer: videojs.Player | null = null
  44 +
  45 +//options配置
  46 +const options: VideoJsPlayerOptions = {
  47 + language: 'zh-CN', // 设置语言
  48 + controls: true, // 是否显示控制条
  49 + preload: 'auto', // 预加载
  50 + autoplay: true, // 是否自动播放
  51 + fluid: false, // 自适应宽高
  52 + poster: props?.avatar || '',
  53 + src: props?.sourceSrc || '', // 要嵌入的视频源的源 URL
  54 + muted: true,
  55 + userActions: {
  56 + hotkeys: true
  57 + }
  58 +}
  59 +
  60 +// 初始化videojs
  61 +const initVideo = () => {
  62 + if (videoRef.value) {
  63 + // 创建 video 实例
  64 + videoPlayer = videojs(videoRef.value, options)
  65 + }
  66 +}
  67 +
  68 +watch(
  69 + () => props.sourceSrc,
  70 + (newData: any) => {
  71 + // props.sourceSrc = newData
  72 + videoPlayer?.src(newData) as any
  73 + videoPlayer?.play()
  74 + },
  75 + {
  76 + immediate: true
  77 + }
  78 +)
  79 +
  80 +onMounted(() => {
  81 + initVideo()
  82 +})
  83 +
  84 +onUnmounted(() => {
  85 + handleVideoDispose()
  86 +})
  87 +
  88 +//播放
  89 +const handleVideoPlay = () => videoPlayer?.play()
  90 +
  91 +const handleVideoDispose = () => videoPlayer?.dispose() && videoPlayer?.pause()
  92 +//暂停
  93 +defineExpose({
  94 + handleVideoPlay,
  95 + handleVideoDispose
  96 +})
  97 +</script>
  98 +
  99 +<style lang="scss" scoped>
  100 +.go-content-box {
  101 + display: flex;
  102 + align-items: center;
  103 + justify-content: center;
  104 + .my-video {
  105 + width: 100% !important;
  106 + height: 100% !important;
  107 + position: relative;
  108 + }
  109 +}
  110 +</style>
... ...
... ... @@ -8,7 +8,7 @@
8 8 </n-input-group>
9 9 </setting-item>
10 10 <setting-item>
11   - <n-button v-if="optionData.dataset.length < 5" size="small" @click="optionData.dataset.push({ url: '' })">
  11 + <n-button v-if="optionData.dataset.length < 9" size="small" @click="optionData.dataset.push({ url: '' })">
12 12 +
13 13 </n-button>
14 14 </setting-item>
... ...
1 1 <template>
2 2 <div class="banner-box" ref="root">
3   - <div class="wrapper">
4   - <div v-for="(item, index) in option.dataset" :key="index + item" :class="item.className" :style="item.sty">
5   - <CameraItem
6   - ref="cameraRef"
7   - :name="item.name"
8   - :avatar="item.avatar"
9   - :key="item + index"
10   - :sourceSrc="item.url"
11   - :w="w"
12   - :h="h"
13   - :index="index"
14   - />
15   - <span class="video-title">{{ item.name }}</span>
16   - </div>
17   - </div>
18   - <a href="javascript:;" class="left" @click="changeSlide('left')"></a>
19   - <a href="javascript:;" class="right" @click="changeSlide('right')"></a>
  3 + <n-grid x-gap="12" :y-gap="12" :cols="computedCols">
  4 + <n-gi v-for="(item, index) in option.dataset" :key="index + item">
  5 + <div class="camera-container">
  6 + <CameraItem
  7 + ref="cameraRef"
  8 + :name="item.name"
  9 + :avatar="item.avatar"
  10 + :key="item + index"
  11 + :sourceSrc="item.url"
  12 + :index="index"
  13 + />
  14 + </div>
  15 + </n-gi>
  16 + </n-grid>
20 17 </div>
21 18 </template>
22 19 <script setup lang="ts">
23   -import { PropType, watch, toRefs, shallowReactive, onMounted, ref } from 'vue'
  20 +import { PropType, toRefs, watch, shallowReactive, ref, computed } from 'vue'
24 21 import { CreateComponentType } from '@/packages/index.d'
25 22 import 'video.js/dist/video-js.min.css'
26 23 import { option as configOption } from './config'
... ... @@ -33,68 +30,34 @@ const props = defineProps({
33 30 }
34 31 })
35 32
36   -const { w, h } = toRefs(props.chartConfig.attr)
  33 +const { h } = toRefs(props.chartConfig.attr)
  34 +
  35 +const responsiveComputeValue = ref(0)
37 36
38 37 const option = shallowReactive({
39 38 dataset: configOption.dataset
40 39 })
41 40
42   -const cameraRef = ref<InstanceType<typeof CameraItem>>()
43   -
44   -let initial = ref(0)
  41 +const computedCols = computed(() => {
  42 + if (option.dataset.length <= 1) return 1
  43 + if (option.dataset.length <= 4) return 2
  44 + return 3
  45 +})
45 46
46   -let interval = ref(2500)
  47 +const cameraRef = ref<InstanceType<typeof CameraItem>>()
47 48
48   -const computedFunc = (initial: number, source: any) => {
49   - if (initial < 0) initial = 0
50   - if (Array.isArray(source)) {
51   - let len = source.length,
52   - temp1 = initial - 2 < 0 ? initial - 2 + len : initial - 2,
53   - temp2 = initial - 1 < 0 ? initial - 1 + len : initial - 1,
54   - temp3 = initial,
55   - temp4 = initial + 1 >= len ? initial + 1 - len : initial + 1,
56   - temp5 = initial + 2 >= len ? initial + 2 - len : initial + 2
57   - return source?.map((item: any, index: number) => {
58   - let transform = `translateX(-50%) scale(0.7)`,
59   - zIndex = 0,
60   - className = 'slide'
61   - switch (index) {
62   - case temp3:
63   - transform = `translateX(-50%) scale(1)`
64   - className = ['slide', 'activate'] as any
65   - zIndex = 300
66   - break
67   - case temp1:
68   - transform = `translateX(-80%) scale(0.7)`
69   - zIndex = 100
70   - break
71   - case temp5:
72   - transform = `translateX(100%) scale(0.7)`
73   - zIndex = 100
74   - break
75   - case temp2:
76   - transform = `translateX(-100%) scale(0.85)`
77   - zIndex = 200
78   - break
79   - case temp4:
80   - transform = `translateX(58%) scale(0.85)`
81   - zIndex = 200
82   - break
83   - }
84   - item.sty = {
85   - transform,
86   - zIndex
87   - }
88   - item.className = className
89   - return item
90   - })
91   - }
  49 +const responsive = (value: number) => {
  50 + responsiveComputeValue.value = value
  51 + if (option.dataset.length <= 2) responsiveComputeValue.value = value
  52 + if (option.dataset.length > 2 && option.dataset.length <= 4) responsiveComputeValue.value = value / 2.03
  53 + if (option.dataset.length > 4 && option.dataset.length <= 9) responsiveComputeValue.value = value / 3.1
92 54 }
93 55
94 56 watch(
95 57 () => props.chartConfig.option.dataset,
96 58 newData => {
97 59 option.dataset = newData
  60 + responsive(h.value)
98 61 },
99 62 {
100 63 immediate: true,
... ... @@ -102,101 +65,19 @@ watch(
102 65 }
103 66 )
104 67
105   -option.dataset = computedFunc(initial.value, option.dataset)
106   -
107 68 watch(
108   - () => initial.value,
109   - newV => {
110   - option.dataset = computedFunc(newV, option.dataset)
  69 + () => h.value,
  70 + newData => responsive(newData),
  71 + {
  72 + immediate: true
111 73 }
112 74 )
113   -
114   -// 处理自动轮播
115   -let timer: any = null
116   -
117   -const autoPlay = () => {
118   - timer = setInterval(() => {
119   - initial.value++
120   - if (initial.value >= option.dataset.length) {
121   - initial.value = 0
122   - }
123   - }, interval.value)
124   -}
125   -
126   -// 鼠标移入移除效果
127   -let root = ref(null)
128   -
129   -onMounted(() => {
130   - clearInterval(timer)
131   - autoPlay()
132   - const box: any = root.value
133   - box.onmouseenter = () => clearInterval(timer)
134   - box.onmouseleave = () => autoPlay()
135   -})
136   -
137   -// 点击左右按钮切换图片
138   -function changeVideo(dir: string) {
139   - if (dir === 'left') {
140   - clearInterval(timer)
141   - initial.value++
142   - initial.value >= option.dataset.length ? (initial.value = 0) : false
143   - return
144   - }
145   - initial.value--
146   - initial.value < 0 ? (initial.value = option.dataset.length - 1) : false
147   -}
148   -
149   -// 左右切换图片设置防抖效果
150   -function changeSlide(dir: string) {
151   - changeVideo(dir)
152   -}
153 75 </script>
154 76
155 77 <style lang="scss" scoped>
156 78 .banner-box {
157   - .wrapper {
158   - height: 100%;
159   - display: flex;
160   - overflow: hidden;
161   - .slide {
162   - width: 20%;
163   - height: 100%;
164   - position: absolute;
165   - left: 10%;
166   - transform: translateX(-50%);
167   - transition: 0.5s;
168   - box-shadow: 0 0 4px black;
169   - .video-title {
170   - width: v-bind('w+"px"');
171   - font-size: 30px;
172   - color: white;
173   - position: absolute;
174   - bottom: 6%;
175   - left: 10%;
176   - z-index: 999;
177   - }
178   - }
179   - }
180   - .arrow {
181   - position: absolute;
182   - top: 50%;
183   - transform: translateY(-50%);
184   - z-index: 9;
185   - width: 50px;
186   - height: 50px;
187   - background-size: contain;
188   - background-color: white;
189   - opacity: 0.5;
190   - }
191   - a.left {
192   - @extend .arrow;
193   - background-image: url(./static/left.svg);
194   - left: 0px;
195   - }
196   - a.right {
197   - @extend .arrow;
198   - background-image: url(./static/right.svg);
199   - right: 0px;
  79 + .camera-container {
  80 + height: v-bind('`${responsiveComputeValue}px`');
200 81 }
201 82 }
202 83 </style>
... ...
  1 +<template>
  2 + <div class="banner-box" ref="root">
  3 + <div class="wrapper">
  4 + <div
  5 + v-for="(item, index) in option.dataset"
  6 + :key="index + item"
  7 + :class="item.className"
  8 + :style="item.sty"
  9 + >
  10 + <CameraItem
  11 + ref="cameraRef"
  12 + :name="item.name"
  13 + :avatar="item.avatar"
  14 + :key="item + index"
  15 + :sourceSrc="item.url"
  16 + :w="w"
  17 + :h="h"
  18 + :index="index"
  19 + />
  20 + <span class="video-title">{{ item.name }}</span>
  21 + </div>
  22 + </div>
  23 + <a href="javascript:;" class="left" @click="changeSlide('left')"></a>
  24 + <a href="javascript:;" class="right" @click="changeSlide('right')"></a>
  25 + </div>
  26 +</template>
  27 +<script setup lang="ts">
  28 +import { PropType, watch, toRefs, shallowReactive, onMounted, ref } from 'vue'
  29 +import { CreateComponentType } from '@/packages/index.d'
  30 +import 'video.js/dist/video-js.min.css'
  31 +import { option as configOption } from './config'
  32 +import { CameraItem } from './components'
  33 +
  34 +const props = defineProps({
  35 + chartConfig: {
  36 + type: Object as PropType<CreateComponentType>,
  37 + required: true
  38 + }
  39 +})
  40 +
  41 +const { w, h } = toRefs(props.chartConfig.attr)
  42 +
  43 +const option = shallowReactive({
  44 + dataset: configOption.dataset
  45 +})
  46 +
  47 +const cameraRef = ref<InstanceType<typeof CameraItem>>()
  48 +
  49 +let initial = ref(0)
  50 +
  51 +let interval = ref(2500)
  52 +
  53 +const computedFunc = (initial: number, source: any) => {
  54 + if (initial < 0) initial = 0
  55 + if (Array.isArray(source)) {
  56 + let len = source.length,
  57 + temp1 = initial - 2 < 0 ? initial - 2 + len : initial - 2,
  58 + temp2 = initial - 1 < 0 ? initial - 1 + len : initial - 1,
  59 + temp3 = initial,
  60 + temp4 = initial + 1 >= len ? initial + 1 - len : initial + 1,
  61 + temp5 = initial + 2 >= len ? initial + 2 - len : initial + 2
  62 + return source?.map((item: any, index: number) => {
  63 + let transform = `translateX(-50%) scale(0.7)`,
  64 + zIndex = 0,
  65 + className = 'slide'
  66 + switch (index) {
  67 + case temp3:
  68 + transform = `translateX(-50%) scale(1)`
  69 + className = ['slide', 'activate'] as any
  70 + zIndex = 300
  71 + break
  72 + case temp1:
  73 + transform = `translateX(-80%) scale(0.7)`
  74 + zIndex = 100
  75 + break
  76 + case temp5:
  77 + transform = `translateX(100%) scale(0.7)`
  78 + zIndex = 100
  79 + break
  80 + case temp2:
  81 + transform = `translateX(-100%) scale(0.85)`
  82 + zIndex = 200
  83 + break
  84 + case temp4:
  85 + transform = `translateX(58%) scale(0.85)`
  86 + zIndex = 200
  87 + break
  88 + }
  89 + item.sty = {
  90 + transform,
  91 + zIndex
  92 + }
  93 + item.className = className
  94 + return item
  95 + })
  96 + }
  97 +}
  98 +
  99 +watch(
  100 + () => props.chartConfig.option.dataset,
  101 + newData => {
  102 + option.dataset = newData
  103 + },
  104 + {
  105 + immediate: true,
  106 + deep: true
  107 + }
  108 +)
  109 +
  110 +option.dataset = computedFunc(initial.value, option.dataset)
  111 +
  112 +watch(
  113 + () => initial.value,
  114 + newV => {
  115 + option.dataset = computedFunc(newV, option.dataset)
  116 + }
  117 +)
  118 +
  119 +// 处理自动轮播
  120 +let timer: any = null
  121 +
  122 +const autoPlay = () => {
  123 + timer = setInterval(() => {
  124 + initial.value++
  125 + if (initial.value >= option.dataset.length) {
  126 + initial.value = 0
  127 + }
  128 + }, interval.value)
  129 +}
  130 +
  131 +// 鼠标移入移除效果
  132 +let root = ref(null)
  133 +
  134 +onMounted(() => {
  135 + clearInterval(timer)
  136 + autoPlay()
  137 + const box: any = root.value
  138 + box.onmouseenter = () => clearInterval(timer)
  139 + box.onmouseleave = () => autoPlay()
  140 +})
  141 +
  142 +// 点击左右按钮切换图片
  143 +function changeVideo(dir: string) {
  144 + if (dir === 'left') {
  145 + clearInterval(timer)
  146 + initial.value++
  147 + initial.value >= option.dataset.length ? (initial.value = 0) : false
  148 + return
  149 + }
  150 + initial.value--
  151 + initial.value < 0 ? (initial.value = option.dataset.length - 1) : false
  152 +}
  153 +
  154 +// 左右切换图片设置防抖效果
  155 +function changeSlide(dir: string) {
  156 + changeVideo(dir)
  157 +}
  158 +</script>
  159 +
  160 +<style lang="scss" scoped>
  161 +.banner-box {
  162 + .wrapper {
  163 + height: 100%;
  164 + display: flex;
  165 + overflow: hidden;
  166 + .slide {
  167 + width: 20%;
  168 + height: 100%;
  169 + position: absolute;
  170 + left: 10%;
  171 + transform: translateX(-50%);
  172 + transition: 0.5s;
  173 + box-shadow: 0 0 4px black;
  174 + .video-title {
  175 + width: v-bind('w+"px"');
  176 + font-size: 30px;
  177 + color: white;
  178 + position: absolute;
  179 + bottom: 6%;
  180 + left: 10%;
  181 + z-index: 999;
  182 + }
  183 + }
  184 + }
  185 + .arrow {
  186 + position: absolute;
  187 + top: 50%;
  188 + transform: translateY(-50%);
  189 + z-index: 9;
  190 + width: 50px;
  191 + height: 50px;
  192 + background-size: contain;
  193 + background-color: white;
  194 + opacity: 0.5;
  195 + }
  196 + a.left {
  197 + @extend .arrow;
  198 + background-image: url(./static/left.svg);
  199 + left: 0px;
  200 + }
  201 + a.right {
  202 + @extend .arrow;
  203 + background-image: url(./static/right.svg);
  204 + right: 0px;
  205 + }
  206 +}
  207 +</style>
... ...