skin.xml + skin.css + skin.js 三层分离的皮肤架构,灵活打造任意界面
skins/myskin/ ├── skin.xml (必需)布局结构:窗口位置 + 子元素坐标 ├── skin.css (必需)视觉样式:按钮、歌词、进度条等 └── skin.js (可选)自定义逻辑:通过 hooks 接管播放器事件
所有元素的 xywh 支持混合单位,实现灵活的响应式布局。
| 单位 | 含义 | 示例 |
|---|---|---|
| 纯数字 | 像素值 | 50 = 50px |
| P | 父容器百分比 | 100P = 100% / 50P = 50% |
| R | 从右/下边反向算像素 | 50R = 距右边/底部 50px |
| C | 水平/垂直居中 | 0C = 在该轴居中 |
| B | border-box:父容器尺寸 - 2×值 | 50B = 父容器 - 100px |
xywh="0C, 50R, 600, 46"
↑ ↑ ↑ ↑
x y w h
x = 0C → 水平居中
y = 50R → 距底部 50px
w = 600 → 宽 600px
h = 46 → 高 46px
5 个顶层窗口:media(视频/封面)、lrc(歌词)、console(控制台)、list(列表)、option(设置)。
<skin name="myskin" version="1.0" author="你的名字">
<!-- ① media 窗口:铺满全屏,放视频/封面/频谱 -->
<media xywh="0, 0, 100P, 100P">
<video xywh="0, 0, 100P, 100P" />
</media>
<!-- ② lrc 窗口:底部居中,高 150px -->
<lrc xywh="0, 200R, 100P, 150">
<text xywh="0, 0, 100P, 100P"
align="center" size="24" color="#888,#fff" leading="36">
</text>
</lrc>
<!-- ③ console 窗口:水平居中,距底 50px -->
<console xywh="0C, 50R, 600, 46">
<bt_play xywh="0, 0, 46, 46" tips="播放/暂停" />
<bt_prev xywh="54, 11, 24, 24" tips="上一首" />
<bt_next xywh="82, 11, 24, 24" tips="下一首" />
<title xywh="116, 11, 200, 24" />
<progress xywh="320, 20, 200, 8" tips="进度" />
<time xywh="528, 15, 60, 16" />
<bt_mute xywh="594, 13, 20, 20" tips="静音" />
<volume xywh="620, 17, 70, 8" tips="音量" />
<bt_list xywh="700, 13, 24, 24" tips="列表" />
</console>
<!-- ④ list 窗口:右侧,上下留 50px,默认隐藏 -->
<list xywh="20R, 50, 260, 50B" display="0">
<tree xywh="0, 0, 100P, 100P" />
</list>
<!-- ⑤ option 窗口:设置面板 -->
<option xywh="20R, 50, 260, 50B" display="0">
<pane xywh="0, 0, 100P, 100P" />
</option>
</skin>
| 标签 | 子元素 | 说明 |
|---|---|---|
<media> | <video> <spectrum> | 视频/封面/频谱容器 |
<lrc> | <text> <kmc> | 歌词显示区 |
<console> | 各种按钮/进度条/文字 | 播放控制台 |
<list> | <tree> | 播放列表 |
<option> | <pane> | 设置面板 |
| 标签 | 说明 | 标签 | 说明 |
|---|---|---|---|
bt_play | 播放/暂停 | bt_prev | 上一首 |
bt_next | 下一首 | bt_mute | 静音 |
volume | 音量条 | progress | 进度条 |
time | 当前/总时长 | title | 歌曲标题 |
status | 状态文字 | bt_list | 列表开关 |
bt_option | 设置开关 | bt_fullscreen | 全屏 |
bt_lrc | 歌词开关 | bt_video | 视频开关 |
tips="..." 属性设置鼠标悬停提示文字。
| 属性 | 说明 | 示例 |
|---|---|---|
| align | 水平对齐 | center / left / right |
| size | 字号(px) | 24 |
| font | 字体 | "楷体" |
| color | 普通色,高亮色 | #888,#fff |
| leading | 行高(px) | 36 |
所有皮肤元素都有统一的 CSS 类名前缀 lmp-。
/* 窗口:透明背景,无边框 */
.lmp-window { background: transparent; border: none; }
/* 按钮:圆形半透明黑底 */
.lmp-btn {
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
cursor: pointer;
transition: background 0.2s;
}
.lmp-btn:hover { background: rgba(0, 0, 0, 0.8); }
.lmp-bt_play { width: 46px; height: 46px; }
/* 歌词行 */
.lmp-lrc-line {
font-size: 22px;
color: rgba(255, 255, 255, 0.5);
text-align: center;
line-height: 1.5;
padding: 4px 0;
transition: all 0.3s;
}
.lmp-lrc-line.lmp-active {
font-size: 28px;
color: #fff;
font-weight: bold;
text-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
}
/* 进度条 */
.lmp-progress-track {
background: rgba(255, 255, 255, 0.3);
border-radius: 4px;
}
.lmp-progress-loaded { background: rgba(255, 255, 255, 0.4); }
.lmp-progress-played { background: #0969da; }
/* 播放列表 */
.lmp-tree {
background: rgba(0, 0, 0, 0.7);
color: #fff;
font-size: 13px;
border-radius: 8px;
padding: 8px;
}
/* 加载动画 */
.lmp-loading {
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
}
通过 LMP34.hooks 监听播放器事件,接管默认行为。不写也能正常播放。
(function () {
var hooks = LMP34.hooks;
var NAMES = hooks.NAMES;
// 播放状态变化:切换按钮图标
hooks.addAction(NAMES.MEDIA_STATE, function (state) {
var btn = document.querySelector('.lmp-bt_play');
if (!btn) return;
// state: 1=播放 2=暂停 3=停止
btn.textContent = (state === 1) ? '❚❚' : '▶';
});
// 歌曲切换:控制台标题
hooks.addAction(NAMES.ITEM_CHANGE, function (item) {
var title = document.querySelector('.lmp-title');
if (title && item) title.textContent = item.title || item.label || '';
});
// 皮肤初始化完成
hooks.addAction(NAMES.SKIN_READY, function () {
console.log('skin loaded');
});
})();
| 类名 | 说明 |
|---|---|
.lmp-window | 所有顶层窗口的公共类 |
.lmp-btn | 所有按钮的公共类 |
.lmp-bt_play | 播放按钮 |
.lmp-bt_prev | 上一首按钮 |
.lmp-bt_next | 下一首按钮 |
.lmp-progress-track | 进度条轨道 |
.lmp-progress-loaded | 已缓冲部分 |
.lmp-progress-played | 已播放部分 |
.lmp-volume-track | 音量条轨道 |
.lmp-lrc-text | 歌词容器 |
.lmp-lrc-line | 每一行歌词 |
.lmp-lrc-line.lmp-active | 当前高亮行 |
.lmp-video-container | 视频容器 |
.lmp-cover | 封面图 |
.lmp-loading | 加载动画 |
.lmp-spectrum | 频谱 canvas |
.lmp-tree | 播放列表树 |
.lmp-item | 列表中的单曲项 |
.lmp-item.lmp-current | 当前播放项 |
LMP34.init({
container: 'lmp34-player',
skins: 'skins/myskin', // 单个皮肤目录
// 或多个:skins: 'skins/default,skins/qqplayer,skins/myskin'
// 默认选第 3 个:skin_id: 3
lists: 'list.xml',
name: '我的播放器',
url: ''
});
| 事件名 | 触发时机 | 回调参数 |
|---|---|---|
| MEDIA_STATE | 播放状态变化 | state (1播放/2暂停/3停止) |
| ITEM_CHANGE | 切歌 | item 对象 |
| LRC_LOADED | 歌词加载完成 | 歌词文本 |
| LRC_RENDER_LINES | 歌词行渲染后 | lines 数组, elements |
| SKIN_READY | 皮肤加载完成 | - |
| PLUGIN_READY | 插件加载完成 | 插件名 |