工作台视频列表页新增点赞/评论趋势折线图(lieflat-charts G8 骨架)
- 引入 lieflat-charts 第三方图表技能到 subskills/(SKILL.md+catalog+templates,已删嵌套 .git) - ECharts 6.1.0 本地化至 mcn-work-shop/public/vendor/(零 CDN 依赖,离线可用) - 账号详情视频列表 tab 上方新增趋势图:X=发布时间升序,点赞/评论双序列面积折线 (G8 Rainfall 骨架:双 grid 独立量级、同 X 联动 pointer、tipLight tooltip、quarticOut 入场) - 全量视频数据单独拉取(pageSize=500,不随分页变化);numOf 解析 'x.x万' 文本 - V1.0/SKILL.md 登记 lieflat-charts 外部技能依赖
This commit is contained in:
1 parent
b1602ec173
commit
5c37e489ea
128 files changed
+26762
-1
No files matched your search
@@ -0,0 +1,177 @@
|
||||
/* ═══════════════════════════════════════════════════════════════
|
||||
MONO TOKENS — 风格的唯一正本
|
||||
所有 mono 图表(Chart.js / ECharts / 手写 SVG)共享这一份。
|
||||
任何文件里出现与本文件冲突的颜色、字体、动画参数,以本文件为准。
|
||||
用法:<script src="mono-tokens.js"></script>,全部挂在 window.MONO 上;
|
||||
也可以直接把本文件内容内联进单文件 HTML(开源分发时推荐内联)。
|
||||
═══════════════════════════════════════════════════════════════ */
|
||||
(function (global) {
|
||||
'use strict';
|
||||
|
||||
/* ── 1 · 色板 ──────────────────────────────────────────────
|
||||
纸灰底 + 炭黑墨。没有彩色。明度即数据:最重要 = 最黑。 */
|
||||
const INK = '#1C1C1A'; // 墨:主数据、标题、强调
|
||||
const PAPER = '#F0EFEB'; // 纸:页面底色 = 浅卡底色(卡片无边框,靠留白分卡)
|
||||
const MUTED = '#8F8E88'; // 次级文字、副标题
|
||||
const FAINT = '#C6C5BF'; // 来源行、辅助刻度
|
||||
const GRID = '#DEDDD6'; // 网格线、发丝线
|
||||
|
||||
// 7 级灰阶 ladder:多系列时按重要性从黑到浅分配
|
||||
const L = ['#1C1C1A', '#4A4944', '#6A6963', '#8F8E88', '#B0AFA9', '#C6C5BF', '#D8D7D1'];
|
||||
// 5 级简版(waffle 等少系列场景)
|
||||
const LAD = ['#1C1C1A', '#4A4944', '#8F8E88', '#B0AFA9', '#D8D7D1'];
|
||||
|
||||
// 暗卡(dark card)专用:底 #1C1C1A,其上的"墨"反转为纸色
|
||||
const DARK = {
|
||||
bg: '#1C1C1A',
|
||||
ink: '#F0EFEB', // 暗卡上的主数据色
|
||||
muted: '#8F8E88',
|
||||
faint: '#55554F', // 暗卡来源行
|
||||
grid: '#2E2D29', // 暗卡网格 / 发丝线
|
||||
gridSoft: '#2A2925',
|
||||
ladder: ['#F0EFEB', '#DCDAD2', '#C9C7BD', '#B3B0A4', '#8F8E88', '#6A6963', '#4A4944'],
|
||||
};
|
||||
|
||||
/* ── 2 · 字体 ────────────────────────────────────────────── */
|
||||
const FONT = {
|
||||
family: 'Inter',
|
||||
// Google Fonts 引入行(放 <head>):
|
||||
link: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap',
|
||||
title: { size: 16.5, weight: 700, spacing: '-.02em' }, // 卡内 h2
|
||||
titleBig: { size: 19, weight: 700, spacing: '-.02em' }, // 独立大图 h2
|
||||
sub: { size: 11.5, weight: 400 }, // 副标题(图例说明写在这里)
|
||||
src: { size: 9.5, weight: 500, spacing: '.08em' }, // 来源行,全大写
|
||||
value: { weight: 800 }, // 图内数值一律 800
|
||||
axis: { size: 9.5, weight: 600 }, // 轴标签
|
||||
// SVG 内最小字号下限:半宽卡 6.5px,通栏/大图 5.5px。低于下限改 hover 出。
|
||||
minHalf: 6.5, minWide: 5.5,
|
||||
};
|
||||
|
||||
/* ── 3 · 形状 ────────────────────────────────────────────── */
|
||||
const SHAPE = {
|
||||
cardRadius: 24, // 卡片圆角
|
||||
cardPad: '28px 28px 20px',
|
||||
barRadius: 99, // 柱端胶囊圆角(竖柱只圆上端,横柱只圆外端)
|
||||
tooltipRadius: 12,
|
||||
};
|
||||
|
||||
/* ── 4 · 动画性格 ────────────────────────────────────────
|
||||
快进快停,quarticOut / cubicOut,不弹跳(elasticOut 只给波浪入场)。 */
|
||||
const MOTION = {
|
||||
enter: 900, // 常规入场 ms
|
||||
enterSlow: 1200, // 大元素 / 关系图
|
||||
easing: 'quarticOut',
|
||||
staggerDot: 12, // 点阵逐个延迟 ms(8–15 区间)
|
||||
staggerBar: 100, // 条形逐根延迟 ms(80–130 区间)
|
||||
// CSS 动画类(手写 SVG 用):pop 缩放入场 / fade 淡入 / draw 描线
|
||||
css: `
|
||||
.pop{transform-box:fill-box;transform-origin:center;animation:pop .5s cubic-bezier(.2,.7,.3,1.3) both}
|
||||
@keyframes pop{from{transform:scale(0)}to{transform:none}}
|
||||
.fade{animation:fade .9s ease both}
|
||||
@keyframes fade{from{opacity:0}}
|
||||
.draw{stroke-dasharray:1;stroke-dashoffset:1;animation:draw 1s cubic-bezier(.4,0,.2,1) both}
|
||||
@keyframes draw{to{stroke-dashoffset:0}}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.pop,.fade{animation:none}
|
||||
.draw{animation:none;stroke-dasharray:none;stroke-dashoffset:0}
|
||||
}`,
|
||||
};
|
||||
|
||||
/* ── 5 · Tooltip ─────────────────────────────────────────
|
||||
浅卡用黑底纸字,暗卡用纸底黑字。ECharts 直接展开这两个对象。 */
|
||||
const tipLight = { backgroundColor: INK, borderWidth: 0, padding: [10, 14],
|
||||
textStyle: { color: PAPER, fontFamily: 'Inter', fontSize: 12 } };
|
||||
const tipDark = { backgroundColor: PAPER, borderWidth: 0, padding: [10, 14],
|
||||
textStyle: { color: INK, fontFamily: 'Inter', fontSize: 12 } };
|
||||
|
||||
/* ── 6 · 确定性伪随机 ────────────────────────────────────
|
||||
演示数据一律用它,不用 Math.random()——刷新必须长一样,
|
||||
否则截图 / 录屏 / 回归对比全部失效。 */
|
||||
const rnd = (i, k) => Math.abs(((i * 73856093) ^ (k * 19349663)) % 1000) / 1000;
|
||||
|
||||
/* ── 7 · 几何 ────────────────────────────────────────────── */
|
||||
const D2R = Math.PI / 180;
|
||||
const pol = (cx, cy, r, deg) => [cx + r * Math.cos(deg * D2R), cy + r * Math.sin(deg * D2R)];
|
||||
// 环形扇区 path(cornerRadius 自己在调用处用 ECharts sector 或手动倒角)
|
||||
const sect = (cx, cy, r0, r1, a0, a1) => {
|
||||
const big = a1 - a0 > 180 ? 1 : 0;
|
||||
const [xa, ya] = pol(cx, cy, r1, a0), [xb, yb] = pol(cx, cy, r1, a1);
|
||||
const [xc, yc] = pol(cx, cy, r0, a1), [xd, yd] = pol(cx, cy, r0, a0);
|
||||
return `M${xa} ${ya} A${r1} ${r1} 0 ${big} 1 ${xb} ${yb} L${xc} ${yc} A${r0} ${r0} 0 ${big} 0 ${xd} ${yd} Z`;
|
||||
};
|
||||
// 手绘感圆(editorial 系气泡用):圆周叠两个慢波 + 噪声,seed 定形
|
||||
const blob = (x, y, r, seed) => {
|
||||
const n = Math.max(14, Math.round(r * 1.6)), pts = [];
|
||||
for (let t = 0; t < n; t++) {
|
||||
const a = t / n * Math.PI * 2;
|
||||
const w = 1 + .055 * Math.sin(a * 2 + seed * 7) + .04 * Math.sin(a * 3 + seed * 13)
|
||||
+ (rnd(seed + t, 3) - .5) * .03;
|
||||
pts.push([x + Math.cos(a) * r * w, y + Math.sin(a) * r * w]);
|
||||
}
|
||||
let d = `M${pts[0][0].toFixed(1)} ${pts[0][1].toFixed(1)}`;
|
||||
for (let t = 0; t < n; t++) {
|
||||
const p = pts[t], q = pts[(t + 1) % n];
|
||||
d += ` Q${p[0].toFixed(1)} ${p[1].toFixed(1)} ${((p[0]+q[0])/2).toFixed(1)} ${((p[1]+q[1])/2).toFixed(1)}`;
|
||||
}
|
||||
return d + ' Z';
|
||||
};
|
||||
|
||||
/* ── 8 · SVG 快捷 ────────────────────────────────────────── */
|
||||
const NS = 'http://www.w3.org/2000/svg';
|
||||
const el = (p, t, a) => { const n = document.createElementNS(NS, t);
|
||||
for (const k in a) n.setAttribute(k, a[k]); p.appendChild(n); return n; };
|
||||
const txt = (p, a, s) => { const n = el(p, 'text', a); n.textContent = s; return n; };
|
||||
const tip = (n, s) => { const t = document.createElementNS(NS, 'title');
|
||||
t.textContent = s; n.appendChild(t); };
|
||||
|
||||
/* ── 9 · 统一 reveal:滚入视野才播,点击重播 ──────────────
|
||||
带 timer 登记(keep),重播前清干净,防动画叠加。 */
|
||||
const timers = {};
|
||||
const keep = (id, t) => { (timers[id] = timers[id] || []).push(t); };
|
||||
const obsReveal = (id, fn) => {
|
||||
const n = document.getElementById(id);
|
||||
const go = () => {
|
||||
(timers[id] || []).forEach(clearInterval); timers[id] = [];
|
||||
if (n.tagName === 'svg' || n.tagName === 'SVG') n.innerHTML = '';
|
||||
fn(n);
|
||||
};
|
||||
const io = new IntersectionObserver(es => {
|
||||
if (es[0].isIntersecting) { go(); io.disconnect(); }
|
||||
}, { threshold: .3 });
|
||||
io.observe(n);
|
||||
n.style.cursor = 'pointer';
|
||||
n.addEventListener('click', go);
|
||||
};
|
||||
// ECharts 版 reveal
|
||||
const eReveal = (id, opt) => obsReveal(id, elDom => {
|
||||
const g = echarts.getInstanceByDom(elDom) || echarts.init(elDom);
|
||||
g.clear(); g.setOption(opt);
|
||||
});
|
||||
|
||||
/* ── 10 · 卡片骨架(生成新图时照抄这个结构) ──────────────
|
||||
<div class="card [dark] [wide]">
|
||||
<h2>结论式标题</h2>
|
||||
<div class="sub">副标题 · 图例说明 · 时间范围</div>
|
||||
<div class="ch" id="xx"></div> 或 <svg id="xx" viewBox="0 0 400 320">
|
||||
<div class="src">图型名 · 系列名 · 数据来源(全大写)</div>
|
||||
</div> */
|
||||
const CARD_CSS = `
|
||||
:root{--bg:${PAPER};--dark:${DARK.bg};--ink:${INK};--muted:${MUTED};--faint:${FAINT};--grid:${GRID}}
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{background:var(--bg);font-family:'Inter',sans-serif;color:var(--ink);padding:40px;-webkit-font-smoothing:antialiased}
|
||||
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:22px;max-width:1400px;margin:0 auto}
|
||||
.card{background:var(--bg);border-radius:${SHAPE.cardRadius}px;padding:${SHAPE.cardPad}}
|
||||
.card.dark{background:var(--dark);color:${PAPER}}
|
||||
.card.dark .sub{color:${MUTED}}
|
||||
.card.dark .src{color:${DARK.faint}}
|
||||
.card.wide{grid-column:1/-1}
|
||||
h2{font-weight:${FONT.title.weight};font-size:${FONT.title.size}px;letter-spacing:${FONT.title.spacing};margin-bottom:3px}
|
||||
.sub{font-size:${FONT.sub.size}px;color:var(--muted);margin-bottom:14px}
|
||||
.src{font-size:${FONT.src.size}px;color:var(--faint);margin-top:10px;letter-spacing:${FONT.src.spacing};font-weight:${FONT.src.weight}}
|
||||
.ch{height:320px}
|
||||
svg text{font-family:'Inter',sans-serif}` + MOTION.css;
|
||||
|
||||
global.MONO = { INK, PAPER, MUTED, FAINT, GRID, L, LAD, DARK,
|
||||
FONT, SHAPE, MOTION, tipLight, tipDark,
|
||||
rnd, pol, sect, blob, el, txt, tip, obsReveal, eReveal, keep, CARD_CSS };
|
||||
})(typeof window !== 'undefined' ? window : globalThis);
|
||||
Reference in new issue
Block a user