让花潮播放器用起来更简单一些
本帖最后由 马黑黑 于 2022-11-17 23:58 编辑花潮播放器可以封装成JS插件,这样用起来就会更简单。假设我们已经将圆环播放器封装成了插件,放在根目录下,则可以这样调用插件:
<script src="/hcplayer.js"></script>
其中,hcplayer.js 就是放在根目录下的播放器插件,它其实是一个 JS 文档,将播放器代码按一定格式封装了起来,代码的核心和我们的播放器代码差不多。插件需要使用者提供少量参数,比如本帖要演示的代码,最少只需要一个参数:
<script>
HCPlayer({
lrcAr : ,
});
</script>
这就能驱动帖子的 id="aud" 的 audio 音频控件播放音乐并显示同步歌词。是不是很奇妙?
不过别高兴太早:discuz! 论坛不支持发帖引用任何JS文档,也就是,<script src="/hcplayer.js"></script> 根本就不能启用播放器插件!
不过也别高兴不起来,我们有应对办法:将原始插件代码全部引入,然后照着上面调用插件——
<script>
//插件代码(一整行)
HCPlayer({
lrcAr : ,
});
</script>
这也很酷,两个 <script> 标签变成了一个,而且,使用者不用管弄成了一行的插件代码,只需给出足够的参数便可。
如果使用插件做帖子,帖子代码样式将会是酱紫,彩色部分的代码是使用者自己要做的,代码的后面我还会给出一些解释:
<style>
#papa {
left: 50%;
transform: translateX(-50%);
width: 900px;
height: 600px;
background: gray;
box-shadow: 3px 3px 20px #000;
position: relative;
}
</style>
<div id="papa"></div>
<audio id="aud" src="音频地址" autoplay loop></audio>
<script>
//插件代码(写成一行)
let lrcAr = [
,
];
HCPlayer({
lrcAr : lrcAr,
});
</script>
一些解释:
一、暗红色的代码。帖子的CSS设定,只和帖子相关,没有播放器的任何代码在里面。但我们约定,装载帖子内容的父元素,#papa 选择器是必须的!
二、蓝色代码是HTML代码。第一行是帖子父元素,我们约定,id="papa" 是必须的!帖子的其他内容自己制作并放在 <div id="papa"> 和 </div> 之间;第二行是播放器代码,自动播放、循环播放,我们还约定,audio 代码中,id="aud" 是必须的!
三、关于JS代码。插件代码不用理睬,到时候将代码替换掉注释部分即可,作为使用不用修改任何地方。要修改的是调用插件的代码:
HCPlayer({
lrcAr : ,
});
HCPlayer 是花潮播放器的调用命令,({ ... }) 里, ... 是插件接口,通过相关接口给插件提供数据,插件则将数据处理结果返回渲染页面;接口使用的是对象表达方式,对象子项目之间可以分行,末尾要有小角逗号。上面示例的红色部分,是向插件提供歌词数组数据。
我预设的接口参数目前有三个(注意大小写):
① lrcAr :
必须是数组,即标准的花潮lrc歌词数组,可以只是一个数组元素,比如上面示例的纯音乐标题。歌词数组可以在 HCPlayer({}) 的前面声明,然后将变量放在 lrcAr : 之后。
② player_css :
这是针对播放控制器的参数。如果提供此参数数据,则需要完整的两样东东,即 top(或bottom)和left(或right),还必须配套提供基于 CSS 变量的参数共三个,--track、--prog、--color,分别定义圆环底轨、进度轨道和按钮+文本颜色。看例子理解:
HCPlayer({
lrcAr : ,
player_css : ' bottom: 10px; left: 10px; --track: #ccc; --prog: red; --color: silver;',
});
③ lrc_css :
这是歌词相关样式设置。如果提供此参数,和第 ② 个参数一样,top(或bottom)和left(或right)必须同时提供,此外也必须同时提供基于 CSS 变量的参数一个,--bg,用于定义lrc歌词颜色。看例子:
HCPlayer({
lrcAr : ,
player_css : 'bottom: 10px; left: 10px; --track: #ccc; --prog: red; --color: silver;',
lrc_css: 'bottom: 10px; left: 10px; --bg: green',
});
插件的参数不是必须的!但至少要有这一句,HCPlayer({}); ,插件才能工作。如果不提供任何参数,播放器将以插件默认的设置生成播放器;如果需要提供参数,② player_css 和 ③ lrc_css 必须提供 left(或right)和 top(或bottom)以及 --... 的 css 变量和值,--track 之类的名称是固定的,不能有拼写错误。参数②和③,正如其名称 _css 所暗示的那样,它们的值是CSS描述语句,但头尾要有小角引号,单引号、双引号、反引号均可,但要统一。
上面的介绍,可能会觉得不好懂,没关系,看二楼的演示,再看后面的完整代码,慢慢就能理解,然后按本帖的介绍去做,使用者会发现,做帖时无需去弄太多的基于播放器的代码,给出几个参数便可,从而可以把精力更多地放在帖子内容之上。
<style>
#papa {
left: 50%;
transform: translateX(-50%);
width: 900px;
height: 600px;
background: gray;
box-shadow: 3px 3px 20px #000;
position: relative;
}
</style>
<div id="papa"></div>
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=1990770877.mp3" autoplay loop></audio>
<script>
(function(mkPlayer) {let defaults = {lrcAr : [],lrc_css: 'top: 10px; left: 50%; transform: translateX(-50%); --bg: linear-gradient(180deg,hsla(100,10%,50%,.75),hsla(100,100%,20%,.65));',player_css : ' bottom: 10px; left: calc(50% - 60px); --track: hsla(100,30%,80%,.65); --prog: hsla(100,60%,50%,.55); --color: silver;',playerCode : `<style>#mplayer { position: absolute; }#btnwrap { display: block; fill: var(--color); opacity: .85; cursor: pointer; }#btnwrap:hover { opacity: 1; }#track {fill: none; stroke: var(--track); }#prog {fill: none; stroke: var(--prog); }#tmsg { fill: var(--color); stroke: none; font: bold 1em sans-serif; }#lrc { --motion: cover2; --tt: 1s; --state: running; --bg: red; position: absolute; font: bold 2.4em sans-serif; color: hsl(100,100%,90%); white-space: nowrap;-webkit-background-clip: text; filter: drop-shadow(1px 1px 2px hsla(0,100%,0%,.85)); z-index: 900; }#lrc::before { position: absolute; content: attr(data-lrc); width: 20%; height: 100%; color: transparent; overflow: hidden; white-space: nowrap; background: var(--bg); filter: inherit; -webkit-background-clip: text; animation: var(--motion) var(--tt) linear forwards; animation-play-state: var(--state); }@keyframes cover1 { from { width: 0; } to { width: 100%; } }@keyframes cover2 { from { width: 0; } to { width: 100%; } }</style><svg id="mplayer" width="120" height="120"><g id="circle_wrap" transform="rotate(-90, 60, 60)" style="cursor: pointer;"><circle id="track" cx="60" cy="60" r="50" stroke-width="10" /><circle id="prog" cx="60" cy="60" r="50" stroke-width="10" /></g><g id="btnwrap"><path id="btnplay" d="M 50 50,50 70,70, 60 z"></path><path id="btnpause" d="M 52 50,52 70,57 70,57 50,52 50 z M 60 50,60 70,65 70,65 50,60 50 z" style="display: none;"></path><path d="M 57 50,60 50,60 70,57 70 z" fill="transparent" /></g><path id="curPath" d="M 20 70 Q 60 0 100 70" fill="none" stroke="none"/><path id="durPath" d="M 20 55 Q 60 110 100 55" fill="none" stroke="none"/><g id="tmsg"><text x="34" y="0"><textPath id="curMsg" xlink:href="#curPath" dominant-baseline="text-after-edge">00:00</textPath></text><text x="29" y="0"><textPath id="durMsg" xlink:href="#durPath" dominant-baseline="text-before-edge">00:00</textPath></text></g></svg><div id="lrc" data-lrc="花潮论坛lrc在线">花潮论坛lrc在线</div>`,};let playCode = (user_config) => {let data = Object.assign({},defaults,user_config);papa.innerHTML += data.playerCode;mplayer.style.cssText += data.player_css;lrc.style.cssText += data.lrc_css;let mKey = 0, mFlag = true, cc = {x: 1 * track.getAttribute('cx'),y: 1 * track.getAttribute('cy'),len: track.getTotalLength(),};prog.style.strokeDasharray = prog.style.strokeDashoffset = cc.len;btnwrap.onclick = () => aud.paused ? aud.play() : aud.pause();circle_wrap.onclick = (e) => {let deg = Math.atan2(e.offsetY - cc.y, e.offsetX - cc.x) * 180 / Math.PI;deg += (e.offsetX < cc.x && e.offsetY < cc.y) ? 450 : 90;aud.currentTime = aud.duration * deg / 360;};aud.addEventListener('timeupdate', () => {prog.style.strokeDashoffset = cc.len - cc.len * aud.currentTime / aud.duration;curMsg.textContent = toMin(aud.currentTime);durMsg.textContent = toMin(aud.duration);for (j = 0; j < data.lrcAr.length; j++) {if (aud.currentTime >= data.lrcAr) {if (mKey === j) showLrc(data.lrcAr);else continue;}}});aud.addEventListener('pause', () => mState());aud.addEventListener('play', () => mState());aud.addEventListener('seeked', () => calcKey());let mState = () => aud.paused ? (btnplay.style.display = 'block', btnpause.style.display = 'none', lrc.style.setProperty('--state', 'paused')) : (btnplay.style.display = 'none', btnpause.style.display = 'block', lrc.style.setProperty('--state', 'running'));let showLrc = (time) => {let name = mFlag ? 'cover1' : 'cover2';lrc.innerHTML = lrc.dataset.lrc = data.lrcAr;lrc.style.setProperty('--motion', name);lrc.style.setProperty('--tt', time + 's');lrc.style.setProperty('--state', 'running');mKey += 1;mFlag = !mFlag;};let calcKey = () => {for(j = 0; j < data.lrcAr.length; j ++) {if(aud.currentTime <= data.lrcAr) {mKey = j - 1;break;}}if(mKey <0) mKey = 0;if(mKey > data.lrcAr.length - 1) mKey = data.lrcAr.length - 1;let time = data.lrcAr - (aud.currentTime - data.lrcAr);showLrc(time);};let toMin = (val) => {if (!val) return '00:00';val = Math.floor(val);let min = parseInt(val / 60), sec = parseFloat(val % 60);if(min < 10) min = '0' + min;if(sec < 10) sec = '0' + sec;return min + ':' + sec;}};mkPlayer.HCPlayer = playCode;})(this);
let lrcAr = [
,
];
HCPlayer({
lrcAr : lrcAr,
});
</script>
二楼代码
<style>
#papa {
left: 50%;
transform: translateX(-50%);
width: 900px;
height: 600px;
background: gray;
box-shadow: 3px 3px 20px #000;
position: relative;
}
</style>
<div id="papa"></div>
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=1990770877.mp3" autoplay loop></audio>
<script>
(function(mkPlayer) {let defaults = {lrcAr : [],lrc_css: 'top: 10px; left: 50%; transform: translateX(-50%); --bg: linear-gradient(180deg,hsla(100,10%,50%,.75),hsla(100,100%,20%,.65));',player_css : ' bottom: 10px; left: calc(50% - 60px); --track: hsla(100,30%,80%,.65); --prog: hsla(100,60%,50%,.55); --color: silver;',playerCode : `<style>#mplayer { position: absolute; }#btnwrap { display: block; fill: var(--color); opacity: .85; cursor: pointer; }#btnwrap:hover { opacity: 1; }#track {fill: none; stroke: var(--track); }#prog {fill: none; stroke: var(--prog); }#tmsg { fill: var(--color); stroke: none; font: bold 1em sans-serif; }#lrc { --motion: cover2; --tt: 1s; --state: running; --bg: red; position: absolute; font: bold 2.4em sans-serif; color: hsl(100,100%,90%); white-space: nowrap;-webkit-background-clip: text; filter: drop-shadow(1px 1px 2px hsla(0,100%,0%,.85)); z-index: 900; }#lrc::before { position: absolute; content: attr(data-lrc); width: 20%; height: 100%; color: transparent; overflow: hidden; white-space: nowrap; background: var(--bg); filter: inherit; -webkit-background-clip: text; animation: var(--motion) var(--tt) linear forwards; animation-play-state: var(--state); }@keyframes cover1 { from { width: 0; } to { width: 100%; } }@keyframes cover2 { from { width: 0; } to { width: 100%; } }</style><svg id="mplayer" width="120" height="120"><g id="circle_wrap" transform="rotate(-90, 60, 60)" style="cursor: pointer;"><circle id="track" cx="60" cy="60" r="50" stroke-width="10" /><circle id="prog" cx="60" cy="60" r="50" stroke-width="10" /></g><g id="btnwrap"><path id="btnplay" d="M 50 50,50 70,70, 60 z"></path><path id="btnpause" d="M 52 50,52 70,57 70,57 50,52 50 z M 60 50,60 70,65 70,65 50,60 50 z" style="display: none;"></path><path d="M 57 50,60 50,60 70,57 70 z" fill="transparent" /></g><path id="curPath" d="M 20 70 Q 60 0 100 70" fill="none" stroke="none"/><path id="durPath" d="M 20 55 Q 60 110 100 55" fill="none" stroke="none"/><g id="tmsg"><text x="34" y="0"><textPath id="curMsg" xlink:href="#curPath" dominant-baseline="text-after-edge">00:00</textPath></text><text x="29" y="0"><textPath id="durMsg" xlink:href="#durPath" dominant-baseline="text-before-edge">00:00</textPath></text></g></svg><div id="lrc" data-lrc="花潮论坛lrc在线">花潮论坛lrc在线</div>`,};let playCode = (user_config) => {let data = Object.assign({},defaults,user_config);papa.innerHTML += data.playerCode;mplayer.style.cssText += data.player_css;lrc.style.cssText += data.lrc_css;let mKey = 0, mFlag = true, cc = {x: 1 * track.getAttribute('cx'),y: 1 * track.getAttribute('cy'),len: track.getTotalLength(),};prog.style.strokeDasharray = prog.style.strokeDashoffset = cc.len;btnwrap.onclick = () => aud.paused ? aud.play() : aud.pause();circle_wrap.onclick = (e) => {let deg = Math.atan2(e.offsetY - cc.y, e.offsetX - cc.x) * 180 / Math.PI;deg += (e.offsetX < cc.x && e.offsetY < cc.y) ? 450 : 90;aud.currentTime = aud.duration * deg / 360;};aud.addEventListener('timeupdate', () => {prog.style.strokeDashoffset = cc.len - cc.len * aud.currentTime / aud.duration;curMsg.textContent = toMin(aud.currentTime);durMsg.textContent = toMin(aud.duration);for (j = 0; j < data.lrcAr.length; j++) {if (aud.currentTime >= data.lrcAr) {if (mKey === j) showLrc(data.lrcAr);else continue;}}});aud.addEventListener('pause', () => mState());aud.addEventListener('play', () => mState());aud.addEventListener('seeked', () => calcKey());let mState = () => aud.paused ? (btnplay.style.display = 'block', btnpause.style.display = 'none', lrc.style.setProperty('--state', 'paused')) : (btnplay.style.display = 'none', btnpause.style.display = 'block', lrc.style.setProperty('--state', 'running'));let showLrc = (time) => {let name = mFlag ? 'cover1' : 'cover2';lrc.innerHTML = lrc.dataset.lrc = data.lrcAr;lrc.style.setProperty('--motion', name);lrc.style.setProperty('--tt', time + 's');lrc.style.setProperty('--state', 'running');mKey += 1;mFlag = !mFlag;};let calcKey = () => {for(j = 0; j < data.lrcAr.length; j ++) {if(aud.currentTime <= data.lrcAr) {mKey = j - 1;break;}}if(mKey <0) mKey = 0;if(mKey > data.lrcAr.length - 1) mKey = data.lrcAr.length - 1;let time = data.lrcAr - (aud.currentTime - data.lrcAr);showLrc(time);};let toMin = (val) => {if (!val) return '00:00';val = Math.floor(val);let min = parseInt(val / 60), sec = parseFloat(val % 60);if(min < 10) min = '0' + min;if(sec < 10) sec = '0' + sec;return min + ':' + sec;}};mkPlayer.HCPlayer = playCode;})(this);
let lrcAr = [
,
];
HCPlayer({
lrcAr : lrcAr,
});
</script>
代码中,17就是插件,不用去理睬它。 封装起来真方便,只是,花潮不支持引用JS文档,这个倒是不知道呢。 “如果不提供任何参数,播放器将以插件默认的设置生成播放器”
这个例子就没使用借口参数呢{:4_173:} 红影 发表于 2022-11-18 12:07
封装起来真方便,只是,花潮不支持引用JS文档,这个倒是不知道呢。
论坛发帖,从任意一个地方引用JS文档都是无效的 红影 发表于 2022-11-18 12:09
“如果不提供任何参数,播放器将以插件默认的设置生成播放器”
这个例子就没使用借口参数呢
使用了一个,lrcAr 想起来了,以前使用封装JS的时候,是做完帖子后放在HTML里一起上传的,就规避了不能使用的问题。 红影 发表于 2022-11-18 12:13
想起来了,以前使用封装JS的时候,是做完帖子后放在HTML里一起上传的,就规避了不能使用的问题。
是这样的 黑黑考虑的周到,还留了接口,太好了。要不,把几个常用播放器都封装一下呗,这样多方便啊{:4_173:} 红影 发表于 2022-11-18 12:15
黑黑考虑的周到,还留了接口,太好了。要不,把几个常用播放器都封装一下呗,这样多方便啊
如果全封装在一起,代码量会很大,所以都要封装的话,不同类型的得单独来。我先封装圆环这一个,尽可能简化参数,做好之后再封装其他的。 马黑黑 发表于 2022-11-18 12:10
论坛发帖,从任意一个地方引用JS文档都是无效的
嗯嗯,现在知道了。 马黑黑 发表于 2022-11-18 12:10
使用了一个,lrcAr
这个是必须的啊,剩下的两个不是必须。 马黑黑 发表于 2022-11-18 12:13
是这样的
那,要是有地方上传,这个会变得更简单了吧。 马黑黑 发表于 2022-11-18 12:22
如果全封装在一起,代码量会很大,所以都要封装的话,不同类型的得单独来。我先封装圆环这一个,尽可能简 ...
对对,一个个封装,全放一起不但代码大,而且容易弄糊涂{:4_173:} 红影 发表于 2022-11-18 15:56
对对,一个个封装,全放一起不但代码大,而且容易弄糊涂
需要的接口参数也多 红影 发表于 2022-11-18 15:55
那,要是有地方上传,这个会变得更简单了吧。
这个不是上传不上传的问题,是论坛不给用 红影 发表于 2022-11-18 15:54
这个是必须的啊,剩下的两个不是必须。
可以根据需要整,比如像改变一下歌词颜色、播放器相关颜色什么的 红影 发表于 2022-11-18 15:54
嗯嗯,现在知道了。
{:4_181:}