tcplayer-plugin
Version:
基于腾讯云播放器的组件库,支持Vue2、Vue3和原生JS
490 lines (453 loc) • 15.8 kB
JavaScript
/*!
* tcplayer-plugin v0.1.0
* (c) 2025
* Released under the MIT License.
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue')) :
typeof define === 'function' && define.amd ? define(['exports', 'vue'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.TcPlayer = {}, global.Vue));
})(this, (function (exports, vue) { 'use strict';
// 动态加载资源(JS和CSS)
function loadResource(sources, cb) {
let arr = [...sources];
if (!arr.length) {
cb && cb();
console.log('resouce loaded');
return false;
}
let item = arr.shift();
if (!item) {
loadResource(arr, cb);
return false;
}
if (item.endsWith('.js')) {
getJS(item, () => {
loadResource(arr, cb);
});
} else if (item.endsWith('.css')) {
getCss(item, () => {
loadResource(arr, cb);
});
} else {
console.warn('no suport resource:', item);
loadResource(arr, cb);
}
}
// 动态加载JS文件
function getJS(url, callback) {
if (document.querySelector(`script[src="${url}"]`)) {
console.log(url, ':已存在');
callback && callback();
return false;
}
var script = document.createElement('script'),
fn = callback || function () {};
script.type = 'text/javascript';
//IE
if (script.readyState) {
script.onreadystatechange = function () {
if (script.readyState == 'loaded' || script.readyState == 'complete') {
script.onreadystatechange = null;
fn();
} else {
script.onreadystatechange = null;
fn();
}
};
} else {
//其他浏览器
script.onload = function () {
fn();
};
script.onerror = function (err) {
fn();
console.log(err);
};
}
script.src = url;
document.querySelector('body').appendChild(script);
}
// 动态加载CSS文件
function getCss(url, callback) {
if (document.querySelector(`link[href="${url}"]`)) {
console.log(url, ':已存在');
callback && callback();
return false;
}
var css = document.createElement('link'),
fn = callback || function () {};
css.rel = 'stylesheet';
//IE
if (css.readyState) {
css.onreadystatechange = function () {
if (css.readyState == 'loaded' || css.readyState == 'complete') {
css.onreadystatechange = null;
fn();
} else {
css.onreadystatechange = null;
fn();
}
};
} else {
//其他浏览器
css.onload = function () {
fn();
};
css.onerror = function (err) {
fn();
console.log(err);
};
}
css.href = url;
document.querySelector('head').appendChild(css);
}
// 全局变量
if (typeof window !== 'undefined') {
window._pursuer = window._pursuer || {};
}
var script = {
name: 'TcPlayer', // 组件名称,确保与import时一致
props: {
config: {
type: Object,
default() {
return {};
}
}
},
data() {
return {
id: '',
duration: 0,
currentTime: 0,
hearttime: 20000, // 心跳时间
timers: [],
initShow: true,
_source_loded: false
};
},
watch: {
'config': {
handler(val) {
if (val && this._source_loded) ;
},
deep: true
}
},
mounted() {
if (typeof window === 'undefined') {
return false;
}
this.id = this.getCfPlayerId();
const resources = [];
const tcplayer = '//web.sdk.qcloud.com/player/tcplayer/release/v4.5.2/tcplayer.v4.5.2.min.js';
const tcplayerCss = '//web.sdk.qcloud.com/player/tcplayer/release/v4.5.2/tcplayer.min.css';
const hls = '//web.sdk.qcloud.com/player/tcplayer/release/v4.5.2/libs/hls.min.0.13.2m.js';
if (!window.TCPlayer) {
resources.push(tcplayerCss, hls, tcplayer);
}
loadResource(resources, () => {
this._source_loded = true;
this.$nextTick(() => {
this.init();
});
});
this.$playerBox = document;
this.$playerBox.addEventListener('keydown', this.onkeydown, true);
this.$playerBox.addEventListener('keyup', this.onkeyup, true);
},
// Vue 2生命周期
beforeDestroy() {
this.destroy();
},
// Vue 3生命周期
beforeUnmount() {
this.destroy();
},
methods: {
getCfPlayerId() {
const $pursuer = typeof window !== 'undefined' ? window._pursuer : {};
$pursuer.playerCount = $pursuer.playerCount ? $pursuer.playerCount + 1 : 1;
const id = this.config && this.config.id || 'player-container-id';
return id;
},
init() {
if (!document.querySelector(`#${this.id}`)) {
return console.log(`#${this.id} dom is not found`);
}
const playerConfig = {
width: '686px',
height: '450px',
reportable: true,
};
this.initPlayer({
...playerConfig,
...(this.config.tconfig || {})
});
},
initPlayer(config) {
if (this.$player) {
this.destroy();
console.log('run tcplayer destroy');
}
if (!window.TCPlayer) {
console.log('TCPlayer not found in window');
return false;
}
this.hearttime = this.config.hearttime || this.hearttime;
console.log('init player config:', config);
this.runs(config);
},
runs(config) {
this.$player = new window.TCPlayer(this.id, config);
this.$player.on('ready', this.onready);
this.$player.on('error', this.onerror);
this.$player.on('timeupdate', this.ontimeupdate);
this.$player.on('pause', this.onpause);
this.$player.on('play', this.onplay);
this.$player.on('ended', this.onended);
this.$player.on('waiting', this.onwaiting);
this.$player.on('snapshoted', this.onsnapshoted);
this.$player.on('sourceloaded', this.onsourceloaded);
// API兼容处理
this.$player.getDuration = this.$player.duration;
this.$player.getCurrentTime = this.$player.currentTime;
this.$player._el = this.$player.el_;
this.$player.seek = this.$player.currentTime;
this.$player.getStatus = this.$player.getStatus || function() {
return this.$player.paused() ? 'pause' : 'playing';
}.bind(this);
this.$player.destroy = this.$player.dispose;
this.$player._originalPlaybackRate = this.$player.playbackRate;
this.$player.getVolume = this.$player.volume;
this.$player.setVolume = this.$player.volume;
},
onsourceloaded(params) {
const paramData = params.paramData;
const desc = paramData.desc;
const definition = paramData.definition;
// 获取清晰度组件并调用清晰度组件的 setCurrentQuality 设置清晰度
try {
const qualityComponent = this.$player.getComponent('QualityComponent');
if (qualityComponent && typeof qualityComponent.setCurrentQuality === 'function') {
qualityComponent.setCurrentQuality(desc, definition);
}
} catch(e) {
console.warn('设置清晰度失败:', e);
}
},
onready() {
console.log('onready:', this.$player);
// 开始执行心跳
this.onheart(true);
this.$emit('onready', this.$player);
this.config.onready && this.config.onready(this.$player);
},
onerror(err) {
console.log('error:', err);
this.$emit('onerror', this.$player);
this.config.onerror && this.config.onerror(this.$player);
},
ontimeupdate() {
this.onheart();
this.duration = this.$player.getDuration();
this.currentTime = this.$player.getCurrentTime();
this.$emit('ontimeupdate', this.duration, this.currentTime, this.$player);
this.config.ontimeupdate && this.config.ontimeupdate(this.duration, this.currentTime, this.$player);
},
onpause() {
console.log('onpause:', this.$player);
this.$emit('onpause', this.$player);
this.config.onpause && this.config.onpause(this.$player);
},
onplay() {
console.log('onplay:');
this.$emit('onplay', this.$player);
this.config.onplay && this.config.onplay(this.$player);
},
onended() {
console.log('ended');
// 结束执行心跳
this.onheart(true);
this.$emit('onended', this.$player);
this.config.onended && this.config.onended(this.$player);
},
onnext() {
this.$emit('onnext', this.$player);
this.config.onnext && this.config.onnext(this.$player);
},
onpre() {
this.$emit('onpre', this.$player);
this.config.onpre && this.config.onpre(this.$player);
},
onwaiting() {
this.$emit('onwaiting', this.$player);
this.config.onwaiting && this.config.onwaiting(this.$player);
},
// 截图
onsnapshoted(data) {
this.$emit('onsnapshoted', data, this.$player);
this.config.onsnapshoted && this.config.onsnapshoted(data, this.$player);
},
// 心跳
onheart(novalite) {
if (novalite) {
this.$emit('onheart', this.$player);
this.config.onheart && this.config.onheart(this.$player);
return false;
}
if (this.onheart_lock) {
return false;
}
clearTimeout(this.timers[0]);
this.timers[0] = null;
this.onheart_lock = true;
this.timers[0] = setTimeout(() => {
this.onheart_lock = false;
this.$emit('onheart', this.$player);
this.config.onheart && this.config.onheart(this.$player);
}, this.hearttime);
},
destroy() {
if (this.$player) {
clearTimeout(this.timers[0] || null);
this.onheart_lock = false;
this.$player.off('ready', this.onready);
this.$player.off('error', this.onerror);
this.$player.off('timeupdate', this.ontimeupdate);
this.$player.off('pause', this.onpause);
this.$player.off('play', this.onplay);
this.$player.off('ended', this.onended);
this.$player.off('waiting', this.onwaiting);
this.$player.off('snapshoted', this.onsnapshoted);
this.$player.off('sourceloaded', this.onsourceloaded);
try {
this.$player.destroy();
} catch (e) {
console.log('destroy error:', e);
}
this.$player = null;
}
if (this.$playerBox) {
this.$playerBox.removeEventListener('keydown', this.onkeydown, true);
this.$playerBox.removeEventListener('keyup', this.onkeyup, true);
this.$playerBox = null;
}
}
}
};
const _hoisted_1 = { class: "ps-player" };
const _hoisted_2 = ["id"];
function render(_ctx, _cache, $props, $setup, $data, $options) {
return (vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [
($data.initShow)
? (vue.openBlock(), vue.createElementBlock("video", {
key: 0,
id: "player-container-id",
preload: "auto",
playsinline: "",
"webkit-playsinline": "",
tabindex: "1",
ref: "playerBox",
class: "ps-player-container"
}, null, 8 /* PROPS */, _hoisted_2))
: vue.createCommentVNode("v-if", true)
]))
}
script.render = render;
script.__file = "src/tcplayer.vue";
// 项目版本号,与package.json保持一致
const version = '0.1.0';
// 检测Vue版本
const isVue2 = vue => vue && !!vue.prototype;
const isVue3 = vue => vue && !!vue.config && !!vue.config.globalProperties;
// Vue2和Vue3兼容安装函数
const install = function (app, options = {}) {
if (isVue2(app)) {
// Vue 2安装方式
app.component(script.name, script);
} else if (isVue3(app)) {
// Vue 3安装方式
app.component(script.name, script);
} else {
console.error('[TcPlayer] 不支持的Vue版本,请使用Vue 2.x或3.x');
}
};
// 为Vue2创建一个具有install方法的对象
script.install = install;
// 自动安装(当通过script标签引入时)
if (typeof window !== 'undefined' && window.Vue) {
if (isVue2(window.Vue)) {
window.Vue.use(script);
} else if (isVue3(window.Vue)) {
window.Vue.use({
install
});
}
}
// 原生JS用法
class TcPlayerJS {
constructor(element, options) {
if (typeof element === 'string') {
element = document.querySelector(element);
}
if (!element) {
throw new Error('[TcPlayer] 目标DOM元素不存在');
}
this.element = element;
this.options = options || {};
this._init();
}
_init() {
// 将Vue组件渲染到目标元素
const container = document.createElement('div');
container.className = 'tc-player-container';
this.element.appendChild(container);
// 这里使用运行时API创建组件实例
if (window.Vue) {
if (isVue3(window.Vue)) {
const app = window.Vue.createApp({
render: () => window.Vue.h(script, {
props: this.options
})
});
this.instance = app.mount(container);
} else if (isVue2(window.Vue)) {
const VueConstructor = window.Vue;
const Component = VueConstructor.extend({
render: h => h(script, {
props: this.options
})
});
this.instance = new Component().$mount(container);
}
} else {
console.error('[TcPlayer] 请先引入Vue.js');
}
}
destroy() {
if (this.instance && this.instance.$destroy) {
this.instance.$destroy();
}
if (this.element) {
this.element.innerHTML = '';
}
}
}
// 默认导出一个具有Vue插件格式的对象
const plugin = {
install,
version,
TcPlayer: script,
TcPlayerJS
};
// 确保为Vue2提供一个可用的install方法
plugin.install = install;
exports.TcPlayer = script;
exports.TcPlayerJS = TcPlayerJS;
exports.default = plugin;
exports.install = install;
exports.version = version;
Object.defineProperty(exports, '__esModule', { value: true });
}));