@nutui/nutui
Version:
京东风格的轻量级移动端 Vue2、Vue3 组件库(支持小程序开发)
450 lines (449 loc) • 15.1 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
import { reactive, ref, computed, watch, nextTick, onMounted, toRefs, openBlock, createElementBlock, createElementVNode, createCommentVNode, withDirectives, vShow, normalizeClass, toDisplayString, normalizeStyle, withModifiers } from "vue";
import { c as createComponent } from "../component-DQf3CENX.js";
import { u as useLocale } from "../index-BOB4ytqZ.js";
import { _ as _export_sfc } from "../_plugin-vue_export-helper-1tPrXgE0.js";
function throttle(fn, delay) {
let timer = null;
let startTime = Date.now();
return function(...args) {
const now = Date.now();
const remaining = delay - (now - startTime);
if (timer) clearTimeout(timer);
if (remaining <= 0) {
fn(...args);
startTime = Date.now();
} else {
timer = setTimeout(fn, remaining);
}
};
}
const { create } = createComponent("video");
const cN = "NutVideo";
const _sfc_main = create({
props: {
source: {
type: Object,
default: {}
},
options: {
type: Object,
default: {
autoplay: false,
// 是否自动播放
volume: 0.5,
poster: "",
loop: false,
controls: true,
muted: false,
// 是否静音
disabled: false,
// 禁止操作
playsinline: false,
// 行内展示
touchPlay: false,
preload: ""
},
required: true
},
model: {
type: String,
default: ""
}
},
components: {},
emits: ["click", "play", "pause", "playend", "time"],
setup(props, { emit, expose }) {
const translate = useLocale(cN);
const state = reactive({
videoElm: null,
initial: true,
// 控制封面的显示
showToolbox: false,
// 控制控制器和标题的显示
// 视频容器元素
player: {
$player: null,
pos: null
},
// progress进度条元素
progressBar: {
progressElm: null,
// 进度条DOM对象
pos: null
},
// video控制显示设置
videoSet: {
loaded: 0,
// 缓存长度
displayTime: "00:00",
// 进度时间
totalTime: "00:00",
// 总时间
progress: {
width: 0,
// 进度条长度
current: 0
// 进度条当前位置
}
},
state: {
controlShow: true,
vol: 0.5,
// 音量
currentTime: 0,
// 当前时间
fullScreen: false,
playing: false,
// 是否正在播放
isLoading: false,
isEnd: false,
isError: false,
isMuted: false
},
showTouchMask: false
});
const root = ref(null);
const isDisabled = computed(() => {
return props.options.disabled;
});
watch(
() => props.source,
(newValue) => {
if (newValue.src) {
nextTick(() => {
state.videoElm.load();
});
}
},
{ immediate: true, deep: true }
);
watch(
() => props.options,
(newValue) => {
state.state.isMuted = newValue ? newValue.muted : false;
},
{ immediate: true }
);
const init = () => {
state.videoElm = root.value;
if (props.options.autoplay) {
setTimeout(() => {
state.videoElm.play();
}, 200);
}
if (props.options.touchPlay) {
state.showTouchMask = true;
}
if (props.options.playsinline) {
state.videoElm.setAttribute("playsinline", props.options.playsinline);
state.videoElm.setAttribute("webkit-playsinline", props.options.playsinline);
state.videoElm.setAttribute("x5-video-player-type", "h5-page");
state.videoElm.setAttribute("x5-video-player-fullscreen", false);
}
volumeHandle();
if (state.showToolbox) {
customerInit();
} else {
state.videoElm.addEventListener("play", () => {
state.state.playing = true;
emit("play", state.videoElm);
});
state.videoElm.addEventListener("pause", () => {
state.state.playing = false;
emit("pause", state.videoElm);
});
state.videoElm.addEventListener("ended", playEnded);
state.videoElm.addEventListener("timeupdate", throttle(getPlayTime, 1e3));
}
};
const customerInit = () => {
const $player = root.value;
const $progress = root.value.getElementsByClassName("nut-video-controller__progress-value")[0];
state.player.$player = $player;
state.progressBar.progressElm = $progress;
state.progressBar.pos = $progress.getBoundingClientRect();
state.videoSet.progress.width = Math.round($progress.getBoundingClientRect().width);
};
const play = () => {
if (props.options.autoplay && props.options.disabled) {
state.state.playing = true;
return false;
}
state.state.playing = !state.state.playing;
if (state.videoElm) {
if (state.state.playing) {
try {
setTimeout(() => {
state.videoElm.play();
}, 200);
state.videoElm.addEventListener("progress", () => {
getLoadTime();
});
state.videoElm.addEventListener("timeupdate", throttle(getPlayTime, 1e3));
state.videoElm.addEventListener("ended", playEnded);
emit("play", state.videoElm);
} catch (e) {
handleError();
}
} else {
state.videoElm.pause();
emit("pause", state.videoElm);
}
}
};
const timeFormat = (t) => {
var h = Math.floor(t / 3600);
if (+h < 10) {
h = "0" + h;
}
var m = Math.floor(t % 3600 / 60);
if (+m < 10) {
m = "0" + m;
}
var s = Math.round(t % 3600 % 60);
if (+s < 10) {
s = "0" + s;
}
var str = "";
if (h != 0) {
str = h + ":" + m + ":" + s;
} else {
str = m + ":" + s;
}
return str;
};
const getLoadTime = () => {
if (state.videoSet.loaded)
state.videoSet.loaded = state.videoElm.buffered.end(0) / state.videoElm.duration * 100;
};
const getPlayTime = () => {
const percent = state.videoElm.currentTime / state.videoElm.duration;
state.videoSet.progress.current = Math.round(state.videoSet.progress.width * percent);
state.videoSet.totalTime = timeFormat(state.videoElm.duration);
state.videoSet.displayTime = timeFormat(state.videoElm.currentTime);
emit("time", state.videoSet.displayTime, state.videoSet.totalTime);
};
const playEnded = () => {
state.state.playing = false;
state.state.isEnd = true;
state.videoSet.displayTime = "00:00";
state.videoSet.progress.current = 0;
state.videoElm.currentTime = 0;
emit("playend", state.videoElm);
};
const handleError = () => {
state.state.isError = true;
};
const volumeHandle = () => {
state.state.vol = props.options.volume;
};
const handleMuted = () => {
state.state.isMuted = !state.state.isMuted;
state.videoElm.muted = state.state.isMuted;
};
const touchSlidSrart = () => {
};
const touchSlidMove = (e) => {
let currentX = e.targetTouches[0].pageX;
let offsetX = currentX - state.progressBar.pos.left;
if (offsetX <= 0) {
offsetX = 0;
}
if (offsetX >= state.videoSet.progress.width) {
offsetX = state.videoSet.progress.width;
}
state.videoSet.progress.current = offsetX;
let percent = state.videoSet.progress.current / state.videoSet.progress.width;
state.videoElm.duration && setPlayTime(percent, state.videoElm.duration);
};
const touchSlidEnd = (e) => {
let currentX = e.changedTouches[0].pageX;
let offsetX = currentX - state.progressBar.pos.left;
state.videoSet.progress.current = offsetX;
let percent = offsetX / state.videoSet.progress.width;
state.videoElm.duration && setPlayTime(percent, state.videoElm.duration);
};
const setPlayTime = (percent, totalTime) => {
state.videoElm.currentTime = Math.floor(percent * totalTime);
};
const retry = () => {
state.state.isError = false;
init();
};
const fullScreen = () => {
if (!state.state.fullScreen) {
state.state.fullScreen = true;
state.videoElm.webkitRequestFullScreen();
} else {
state.state.fullScreen = false;
document.webkitCancelFullScreen();
}
};
const pause = () => {
state.state.playing = false;
state.videoElm.pause();
emit("pause", state.videoElm);
};
const stop = () => {
playEnded();
state.videoElm.pause();
};
const muted = () => {
state.state.isMuted = true;
state.videoElm.muted = true;
};
const unmuted = () => {
state.state.isMuted = false;
state.videoElm.muted = false;
};
expose({
play,
pause,
stop,
muted,
unmuted
});
onMounted(() => {
init();
});
return __spreadProps(__spreadValues(__spreadValues({
root
}, toRefs(props)), toRefs(state)), {
handleError,
isDisabled,
play,
handleMuted,
touchSlidSrart,
touchSlidMove,
touchSlidEnd,
retry,
fullScreen,
translate
});
}
});
const _hoisted_1 = {
ref: "videocon",
class: "nut-video"
};
const _hoisted_2 = ["muted", "autoplay", "loop", "poster", "controls", "preload"];
const _hoisted_3 = ["src", "type"];
const _hoisted_4 = { class: "nut-video-controller__now" };
const _hoisted_5 = { class: "nut-video-controller__progress" };
const _hoisted_6 = {
ref: "progressBar",
class: "nut-video-controller__progress-value"
};
const _hoisted_7 = {
ref: "playedBar",
class: "nut-video-controller__played"
};
const _hoisted_8 = { class: "nut-video-controller__total" };
const _hoisted_9 = { class: "nut-video-error" };
const _hoisted_10 = { class: "nut-video-error-tip" };
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return openBlock(), createElementBlock("div", _hoisted_1, [
createElementVNode("video", {
ref: "root",
class: "nut-video-player",
muted: _ctx.options.muted,
autoplay: _ctx.options.autoplay,
loop: _ctx.options.loop,
poster: _ctx.options.poster,
controls: _ctx.options.controls,
preload: _ctx.options.preload,
onError: _cache[0] || (_cache[0] = (...args) => _ctx.handleError && _ctx.handleError(...args))
}, [
createElementVNode("source", {
src: _ctx.source.src,
type: _ctx.source.type
}, null, 8, _hoisted_3)
], 40, _hoisted_2),
_ctx.showToolbox && !_ctx.isDisabled ? (openBlock(), createElementBlock("div", {
key: 0,
ref: "touchMask",
class: "nut-video-mask",
onClick: _cache[1] || (_cache[1] = (...args) => _ctx.play && _ctx.play(...args))
}, null, 512)) : createCommentVNode("", true),
_ctx.showToolbox && !_ctx.isDisabled ? withDirectives((openBlock(), createElementBlock("div", {
key: 1,
ref: "palyBtn",
class: "nut-video-play-btn",
onClick: _cache[2] || (_cache[2] = (...args) => _ctx.play && _ctx.play(...args))
}, null, 512)), [
[vShow, !_ctx.state.playing]
]) : createCommentVNode("", true),
withDirectives(createElementVNode("div", {
class: normalizeClass(["nut-video-controller", { "nut-video-controller--show": !_ctx.state.playing, "nut-video-controller--hide": _ctx.state.playing }])
}, [
createElementVNode("div", {
class: "nut-video-controller__playbtn",
onClick: _cache[3] || (_cache[3] = (...args) => _ctx.play && _ctx.play(...args))
}),
createElementVNode("div", _hoisted_4, toDisplayString(_ctx.videoSet.displayTime), 1),
createElementVNode("div", _hoisted_5, [
createElementVNode("div", _hoisted_6, [
createElementVNode("div", {
class: "buffered",
style: normalizeStyle({ width: `${_ctx.videoSet.loaded}%` })
}, null, 4),
createElementVNode("div", {
class: "nut-video-controller__ball",
style: normalizeStyle({
transform: `translate3d(${_ctx.videoSet.progress.current}px, -50%, 0)`
}),
onTouchmove: _cache[4] || (_cache[4] = withModifiers(($event) => _ctx.touchSlidMove($event), ["stop", "prevent"])),
onTouchstart: _cache[5] || (_cache[5] = withModifiers(($event) => _ctx.touchSlidSrart(), ["stop"])),
onTouchend: _cache[6] || (_cache[6] = withModifiers(($event) => _ctx.touchSlidEnd($event), ["stop"]))
}, _cache[10] || (_cache[10] = [
createElementVNode("div", { class: "nut-video-controller__ball-move" }, null, -1)
]), 36),
createElementVNode("div", _hoisted_7, null, 512)
], 512)
]),
createElementVNode("div", _hoisted_8, toDisplayString(_ctx.videoSet.totalTime), 1),
createElementVNode("div", {
class: normalizeClass(["nut-video-controller__volume", { muted: _ctx.state.isMuted }]),
onClick: _cache[7] || (_cache[7] = (...args) => _ctx.handleMuted && _ctx.handleMuted(...args))
}, null, 2),
createElementVNode("div", {
class: "nut-video-controller__full",
onClick: _cache[8] || (_cache[8] = (...args) => _ctx.fullScreen && _ctx.fullScreen(...args))
})
], 2), [
[vShow, _ctx.showToolbox && !_ctx.isDisabled]
]),
withDirectives(createElementVNode("div", _hoisted_9, [
createElementVNode("p", _hoisted_10, toDisplayString(_ctx.translate("errorTip")), 1),
createElementVNode("p", {
class: "nut-video-error-retry",
onClick: _cache[9] || (_cache[9] = (...args) => _ctx.retry && _ctx.retry(...args))
}, toDisplayString(_ctx.translate("clickRetry")), 1)
], 512), [
[vShow, _ctx.state.isError]
])
], 512);
}
const NutVideo = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
export {
NutVideo as default
};