@aochuang/common 1.0.137 → 1.0.139
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/icon/icon.vue +2 -2
- package/components/image/index.js +5 -1
- package/components/image/util.js +56 -21
- package/components/video/video.vue +258 -17
- package/package.json +1 -1
package/components/icon/icon.vue
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import Image from './image'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
setImageClickFn,
|
|
4
|
+
getThumbnailSrc as _getThumbnailSrc,
|
|
5
|
+
removeThumbnailSrc as _removeThumbnailSrc
|
|
6
|
+
} from './util'
|
|
3
7
|
|
|
4
8
|
Image.install = (Vue, options) => {
|
|
5
9
|
options = Object.assign({
|
package/components/image/util.js
CHANGED
|
@@ -106,6 +106,39 @@ export function getThumbnailSrc (url, width) {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
function decodeUrlParam (value) {
|
|
110
|
+
try {
|
|
111
|
+
return decodeURIComponent(value.replace(/\+/g, ' '))
|
|
112
|
+
} catch (e) {
|
|
113
|
+
return value
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function removeThumbnailQuery (url) {
|
|
118
|
+
const hashIndex = url.indexOf('#')
|
|
119
|
+
const hash = hashIndex > -1 ? url.substr(hashIndex) : ''
|
|
120
|
+
const urlWithoutHash = hashIndex > -1 ? url.substr(0, hashIndex) : url
|
|
121
|
+
const queryIndex = urlWithoutHash.indexOf('?')
|
|
122
|
+
|
|
123
|
+
if (queryIndex < 0) {
|
|
124
|
+
return url
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const baseUrl = urlWithoutHash.substr(0, queryIndex)
|
|
128
|
+
const query = urlWithoutHash.substr(queryIndex + 1)
|
|
129
|
+
const nextQuery = query.split('&').filter(param => {
|
|
130
|
+
const equalIndex = param.indexOf('=')
|
|
131
|
+
const key = decodeUrlParam(equalIndex > -1 ? param.substr(0, equalIndex) : param)
|
|
132
|
+
const value = decodeUrlParam(equalIndex > -1 ? param.substr(equalIndex + 1) : '')
|
|
133
|
+
const isAliyunThumbnail = key === 'x-oss-process' && /^image\/resize,w_[^/]+$/.test(value)
|
|
134
|
+
const isTencentThumbnail = /^imageMogr2\/thumbnail\/[^/]+$/.test(key) && !value
|
|
135
|
+
|
|
136
|
+
return !isAliyunThumbnail && !isTencentThumbnail
|
|
137
|
+
}).join('&')
|
|
138
|
+
|
|
139
|
+
return baseUrl + (nextQuery ? '?' + nextQuery : '') + hash
|
|
140
|
+
}
|
|
141
|
+
|
|
109
142
|
/**
|
|
110
143
|
* 移除图片缩略图裁剪参数,保留其他查询参数及 hash
|
|
111
144
|
*/
|
|
@@ -115,27 +148,29 @@ export function removeThumbnailSrc (url) {
|
|
|
115
148
|
}
|
|
116
149
|
|
|
117
150
|
const hashIndex = url.indexOf('#')
|
|
118
|
-
const hash = hashIndex
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
151
|
+
const hash = hashIndex > -1 ? url.substr(hashIndex) : ''
|
|
152
|
+
const urlWithoutHash = hashIndex > -1 ? url.substr(0, hashIndex) : url
|
|
153
|
+
let proxyPath = ''
|
|
154
|
+
|
|
155
|
+
if (urlWithoutHash.indexOf('/oss/view') > -1) {
|
|
156
|
+
proxyPath = '/oss/view'
|
|
157
|
+
} else if (urlWithoutHash.indexOf('/oss/') > -1) {
|
|
158
|
+
proxyPath = '/oss/'
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!proxyPath) {
|
|
162
|
+
return removeThumbnailQuery(url)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const urlParams = getUrlParams(urlWithoutHash)
|
|
166
|
+
if (!urlParams.url) {
|
|
167
|
+
return url
|
|
132
168
|
}
|
|
133
169
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return source + hash
|
|
170
|
+
return buildUrl(
|
|
171
|
+
urlWithoutHash.substr(0, urlWithoutHash.indexOf(proxyPath) + proxyPath.length),
|
|
172
|
+
Object.assign({}, urlParams, {
|
|
173
|
+
url: removeThumbnailSrc(urlParams.url)
|
|
174
|
+
})
|
|
175
|
+
) + hash
|
|
141
176
|
}
|
|
@@ -1,23 +1,76 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
class="ui-video"
|
|
4
|
-
:
|
|
5
|
-
:
|
|
6
|
-
:poster="poster"
|
|
7
|
-
:preload="preload"
|
|
8
|
-
:height="height"
|
|
9
|
-
:controls="controls"
|
|
10
|
-
@error="handleError"
|
|
2
|
+
<div
|
|
3
|
+
class="ui-video"
|
|
4
|
+
:class="{'is-poster-play-mode': posterPlayMode}"
|
|
5
|
+
:style="wrapperStyle"
|
|
11
6
|
>
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
<template v-if="posterPlayMode">
|
|
8
|
+
<div
|
|
9
|
+
v-if="innerSrc"
|
|
10
|
+
class="ui-video__cover"
|
|
11
|
+
:class="{'is-empty': !innerPoster}"
|
|
12
|
+
:style="coverStyle"
|
|
13
|
+
@click="handlePlayClick"
|
|
14
|
+
>
|
|
15
|
+
<div class="ui-video__cover-mask"></div>
|
|
16
|
+
<div class="ui-video__cover-play">
|
|
17
|
+
<ui-icon name="play-line1" :size="24"></ui-icon>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
<div v-else class="ui-video__poster-only" :style="coverStyle"></div>
|
|
21
|
+
<div v-if="showFloatingPlayer" ref="floatingLayer" class="ui-video__floating-layer" @click.self="handleCloseClick">
|
|
22
|
+
<div class="ui-video__floating-panel">
|
|
23
|
+
<div class="ui-video__floating-head">
|
|
24
|
+
<ui-icon class="ui-video__floating-close" name="close-circle-line" :size="28" @click="handleCloseClick"></ui-icon>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="ui-video__floating-body">
|
|
27
|
+
<video
|
|
28
|
+
ref="floatingVideo"
|
|
29
|
+
class="ui-video__native"
|
|
30
|
+
:autoplay="autoplay"
|
|
31
|
+
:poster="poster"
|
|
32
|
+
:preload="preload"
|
|
33
|
+
:controls="controls"
|
|
34
|
+
playsinline
|
|
35
|
+
webkit-playsinline
|
|
36
|
+
@error="handleError"
|
|
37
|
+
>
|
|
38
|
+
<source :src="innerSrc">
|
|
39
|
+
您的浏览器不支持 video 标签。
|
|
40
|
+
</video>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</template>
|
|
45
|
+
<template v-else>
|
|
46
|
+
<video
|
|
47
|
+
ref="nativeVideo"
|
|
48
|
+
class="ui-video__native"
|
|
49
|
+
:autoplay="autoplay"
|
|
50
|
+
:width="width"
|
|
51
|
+
:poster="poster"
|
|
52
|
+
:preload="preload"
|
|
53
|
+
:height="height"
|
|
54
|
+
:controls="controls"
|
|
55
|
+
playsinline
|
|
56
|
+
webkit-playsinline
|
|
57
|
+
@error="handleError"
|
|
58
|
+
>
|
|
59
|
+
<source :src="innerSrc">
|
|
60
|
+
您的浏览器不支持 video 标签。
|
|
61
|
+
</video>
|
|
62
|
+
</template>
|
|
63
|
+
</div>
|
|
15
64
|
</template>
|
|
16
65
|
<script>
|
|
66
|
+
import UiIcon from '../icon'
|
|
17
67
|
import { formatOssPath } from '../../utils/oss'
|
|
18
68
|
|
|
19
69
|
export default {
|
|
20
70
|
name: 'UiVideo',
|
|
71
|
+
components: {
|
|
72
|
+
UiIcon
|
|
73
|
+
},
|
|
21
74
|
props: {
|
|
22
75
|
width: {
|
|
23
76
|
type: String
|
|
@@ -39,6 +92,10 @@ export default {
|
|
|
39
92
|
type: Boolean,
|
|
40
93
|
default: false
|
|
41
94
|
},
|
|
95
|
+
posterPlayMode: {
|
|
96
|
+
type: Boolean,
|
|
97
|
+
default: false
|
|
98
|
+
},
|
|
42
99
|
src: {
|
|
43
100
|
type: String
|
|
44
101
|
}
|
|
@@ -46,36 +103,92 @@ export default {
|
|
|
46
103
|
computed: {
|
|
47
104
|
style () {
|
|
48
105
|
return {
|
|
49
|
-
width: this.width,
|
|
50
|
-
height: this.height
|
|
106
|
+
width: this.formatSize(this.width),
|
|
107
|
+
height: this.formatSize(this.height)
|
|
51
108
|
}
|
|
52
109
|
},
|
|
110
|
+
wrapperStyle () {
|
|
111
|
+
if (!this.posterPlayMode) {
|
|
112
|
+
return null
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
width: this.formatSize(this.width),
|
|
116
|
+
height: this.formatSize(this.height)
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
innerPoster () {
|
|
120
|
+
return formatOssPath(this.poster)
|
|
121
|
+
},
|
|
122
|
+
coverStyle () {
|
|
123
|
+
const style = {
|
|
124
|
+
width: this.formatSize(this.width),
|
|
125
|
+
height: this.formatSize(this.height)
|
|
126
|
+
}
|
|
127
|
+
if (this.innerPoster) {
|
|
128
|
+
style.backgroundImage = `url(${this.innerPoster})`
|
|
129
|
+
}
|
|
130
|
+
return style
|
|
131
|
+
},
|
|
53
132
|
innerSrc () {
|
|
54
133
|
return formatOssPath(this.src)
|
|
55
134
|
}
|
|
56
135
|
},
|
|
57
136
|
data () {
|
|
58
137
|
return {
|
|
59
|
-
isError: false
|
|
138
|
+
isError: false,
|
|
139
|
+
showFloatingPlayer: false
|
|
60
140
|
}
|
|
61
141
|
},
|
|
62
142
|
watch: {
|
|
63
143
|
src () {
|
|
144
|
+
clearTimeout(this._checkErrorTimer)
|
|
145
|
+
this._checkErrorTimer = null
|
|
64
146
|
this.isError = false
|
|
147
|
+
this.showFloatingPlayer = false
|
|
148
|
+
if (!this.posterPlayMode) {
|
|
149
|
+
this.$nextTick(() => {
|
|
150
|
+
this.loopCheckError()
|
|
151
|
+
})
|
|
152
|
+
}
|
|
65
153
|
}
|
|
66
154
|
},
|
|
67
155
|
mounted () {
|
|
68
|
-
this.
|
|
156
|
+
if (!this.posterPlayMode) {
|
|
157
|
+
this.loopCheckError()
|
|
158
|
+
}
|
|
69
159
|
},
|
|
70
160
|
methods: {
|
|
161
|
+
formatSize (value) {
|
|
162
|
+
if (!value && value !== 0) {
|
|
163
|
+
return null
|
|
164
|
+
}
|
|
165
|
+
if (typeof value === 'number') {
|
|
166
|
+
return `${value}px`
|
|
167
|
+
}
|
|
168
|
+
if (/^\d+$/.test(String(value))) {
|
|
169
|
+
return `${value}px`
|
|
170
|
+
}
|
|
171
|
+
return value
|
|
172
|
+
},
|
|
173
|
+
getVideoElement () {
|
|
174
|
+
if (this.posterPlayMode) {
|
|
175
|
+
return this.$refs.floatingVideo
|
|
176
|
+
}
|
|
177
|
+
return this.$refs.nativeVideo
|
|
178
|
+
},
|
|
71
179
|
loopCheckError () {
|
|
72
180
|
if (this.isError) {
|
|
73
181
|
return
|
|
74
182
|
}
|
|
75
183
|
this._checkErrorTimer = setTimeout(() => {
|
|
184
|
+
const videoEl = this.getVideoElement()
|
|
185
|
+
if (!videoEl) {
|
|
186
|
+
this.loopCheckError()
|
|
187
|
+
return
|
|
188
|
+
}
|
|
76
189
|
// 0:没有关于媒体资源的可用信息,或者媒体资源尚未加载。
|
|
77
190
|
// 3: 媒体元素尝试加载资源,但没有找到有效的媒体源(可能是资源 URL 错误、403/404 等网络错误,或所有<source>标签指定的资源都无法加载)。
|
|
78
|
-
if (
|
|
191
|
+
if (videoEl.readyState === 0 && videoEl.networkState === 3) {
|
|
79
192
|
this.isError = true
|
|
80
193
|
this.$emit('error')
|
|
81
194
|
} else {
|
|
@@ -86,10 +199,48 @@ export default {
|
|
|
86
199
|
handleError () {
|
|
87
200
|
this.isError = true
|
|
88
201
|
this.$emit('error')
|
|
202
|
+
},
|
|
203
|
+
appendFloatingLayer () {
|
|
204
|
+
const layerEl = this.$refs.floatingLayer
|
|
205
|
+
if (!layerEl || !document.body || layerEl.parentNode === document.body) {
|
|
206
|
+
return
|
|
207
|
+
}
|
|
208
|
+
document.body.appendChild(layerEl)
|
|
209
|
+
},
|
|
210
|
+
removeFloatingLayer () {
|
|
211
|
+
const layerEl = this.$refs.floatingLayer
|
|
212
|
+
if (!layerEl || layerEl.parentNode !== document.body) {
|
|
213
|
+
return
|
|
214
|
+
}
|
|
215
|
+
document.body.removeChild(layerEl)
|
|
216
|
+
},
|
|
217
|
+
handlePlayClick () {
|
|
218
|
+
this.showFloatingPlayer = true
|
|
219
|
+
this.$nextTick(() => {
|
|
220
|
+
this.appendFloatingLayer()
|
|
221
|
+
const videoEl = this.$refs.floatingVideo
|
|
222
|
+
if (!videoEl) {
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
if (!this._checkErrorTimer && !this.isError) {
|
|
226
|
+
this.loopCheckError()
|
|
227
|
+
}
|
|
228
|
+
if (videoEl.play) {
|
|
229
|
+
videoEl.play().catch(() => {})
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
},
|
|
233
|
+
handleCloseClick () {
|
|
234
|
+
clearTimeout(this._checkErrorTimer)
|
|
235
|
+
this._checkErrorTimer = null
|
|
236
|
+
this.removeFloatingLayer()
|
|
237
|
+
this.showFloatingPlayer = false
|
|
89
238
|
}
|
|
90
239
|
},
|
|
91
240
|
beforeDestroy () {
|
|
92
241
|
clearTimeout(this._checkErrorTimer)
|
|
242
|
+
this._checkErrorTimer = null
|
|
243
|
+
this.removeFloatingLayer()
|
|
93
244
|
}
|
|
94
245
|
}
|
|
95
246
|
</script>
|
|
@@ -97,5 +248,95 @@ export default {
|
|
|
97
248
|
.ui-video {
|
|
98
249
|
background-color: #000;
|
|
99
250
|
object-fit: contain;
|
|
251
|
+
position: relative;
|
|
252
|
+
z-index: 0;
|
|
253
|
+
display: block;
|
|
254
|
+
-webkit-transform: translateZ(0);
|
|
255
|
+
transform: translateZ(0);
|
|
256
|
+
}
|
|
257
|
+
.ui-video__native{
|
|
258
|
+
display: block;
|
|
259
|
+
max-width: 100%;
|
|
260
|
+
max-height: 100%;
|
|
261
|
+
background-color: #000;
|
|
262
|
+
object-fit: contain;
|
|
263
|
+
}
|
|
264
|
+
.ui-video.is-poster-play-mode{
|
|
265
|
+
overflow: hidden;
|
|
266
|
+
display: block;
|
|
267
|
+
}
|
|
268
|
+
.ui-video__cover{
|
|
269
|
+
position: relative;
|
|
270
|
+
background-color: #000;
|
|
271
|
+
background-position: center;
|
|
272
|
+
background-repeat: no-repeat;
|
|
273
|
+
background-size: cover;
|
|
274
|
+
cursor: pointer;
|
|
275
|
+
display: block;
|
|
276
|
+
}
|
|
277
|
+
.ui-video__cover.is-empty{
|
|
278
|
+
background-image: none;
|
|
279
|
+
}
|
|
280
|
+
.ui-video__poster-only{
|
|
281
|
+
background-color: #000;
|
|
282
|
+
background-position: center;
|
|
283
|
+
background-repeat: no-repeat;
|
|
284
|
+
background-size: cover;
|
|
285
|
+
display: block;
|
|
286
|
+
}
|
|
287
|
+
.ui-video__floating-layer{
|
|
288
|
+
position: fixed;
|
|
289
|
+
inset: 0;
|
|
290
|
+
z-index: 9999;
|
|
291
|
+
background-color: rgba(0,0,0,.72);
|
|
292
|
+
display: flex;
|
|
293
|
+
align-items: center;
|
|
294
|
+
justify-content: center;
|
|
295
|
+
padding: 24px 0;
|
|
296
|
+
box-sizing: border-box;
|
|
297
|
+
}
|
|
298
|
+
.ui-video__floating-panel{
|
|
299
|
+
width: 100%;
|
|
300
|
+
max-width: 960px;
|
|
301
|
+
border-radius: 4px;
|
|
302
|
+
overflow: hidden;
|
|
303
|
+
}
|
|
304
|
+
.ui-video__floating-head{
|
|
305
|
+
display: flex;
|
|
306
|
+
justify-content: flex-end;
|
|
307
|
+
padding: 8px 8px 0;
|
|
308
|
+
color: #fff;
|
|
309
|
+
}
|
|
310
|
+
.ui-video__floating-close{
|
|
311
|
+
cursor: pointer;
|
|
312
|
+
}
|
|
313
|
+
.ui-video__floating-body{
|
|
314
|
+
padding: 8px 0 12px;
|
|
315
|
+
}
|
|
316
|
+
.ui-video__floating-body .ui-video__native{
|
|
317
|
+
width: 100%;
|
|
318
|
+
height: auto;
|
|
319
|
+
max-width: 100%;
|
|
320
|
+
max-height: calc(100vh - 120px);
|
|
321
|
+
}
|
|
322
|
+
.ui-video__cover-mask{
|
|
323
|
+
position: absolute;
|
|
324
|
+
inset: 0;
|
|
325
|
+
background-color: rgba(0,0,0,.2);
|
|
326
|
+
}
|
|
327
|
+
.ui-video__cover-play{
|
|
328
|
+
position: absolute;
|
|
329
|
+
left: 50%;
|
|
330
|
+
top: 50%;
|
|
331
|
+
width: 44px;
|
|
332
|
+
height: 44px;
|
|
333
|
+
margin-left: -22px;
|
|
334
|
+
margin-top: -22px;
|
|
335
|
+
border-radius: 50%;
|
|
336
|
+
background-color: rgba(0,0,0,.55);
|
|
337
|
+
color: #fff;
|
|
338
|
+
display: flex;
|
|
339
|
+
align-items: center;
|
|
340
|
+
justify-content: center;
|
|
100
341
|
}
|
|
101
342
|
</style>
|