PageSourceSearch

https://asic42cn.ok.kimi.link/js/charts/revenue_split.js

js asic42cn.ok.kimi.link collected 2026-10-03 04:51:29 UTC 8,985 bytes, 172 lines download raw bytes

1// The crossover that isn't — TSMC AI revenue by customer, anonymized (v6.1)
2// v6.1: anonymized per editorial policy — GPU vendors A/B, CSP1–CSP3; composition
3// bars show percentages instead of dollar values; analyst-estimates framing.
4// Revision: drills now show percentages only (no absolute $ values on the chart
5// surface or in drill cards); "Nomura" withdrawn from chart copy — the source
6// line retains the attribution per citation discipline.
7// Stacked composition bars 2025/27F (v8.6: 2026F retired) + the ASIC camp's share
8// rising toward a 50% line it never reaches. Data: V52.value_split (Nomura Fig. 35,
9// PDF-verified). Share labels are approximations (~1/4, ~1/3); exact values in drills.
10(() => {
11  const host = document.getElementById("revenue-split-chart");
12  if (!host) return;
13  const D = (window.V52 && V52.value_split) || null;
14  const body = U.frame(host, {
15    title: "并不存在的反超:TSMC AI 收入按客户拆分(分析师估计,未经公司确认)",
16    sub: "占 TSMC AI 收入百分比 · 墨 = GPU 阵营 · 蓝 = ASIC 阵营 · 2025 对 2027F",
17    src: "Broker research",
18  });
19  if (!D) { body.innerHTML = '<p style="font-family:monospace;font-size:12px;color:#8595a6;padding:20px 0">value-split 数据集未加载。</p>'; return; }
20
21  // 匿名化名表(图面专用;drill 保留原值,不印映射)
22  const NAME = { "NVDA": "GPU 厂商 A", "AMD": "GPU 厂商 B", "Google TPU": "CSP1",
23    "AWS Trainium": "CSP2", "Meta MTIA": "CSP3", "HBM base die": "HBM 基础裸片", "Other xPUs": "其他" };
24  const anon = s => String(s).replace(/Google(?!'s)/g, "CSP1").replace(/NVDA/g, "GPU 厂商 A")
25    .replace(/AWS/g, "CSP2").replace(/Meta/g, "CSP3").replace(/AMD/g, "GPU 厂商 B");
26  const SHORT = { "GPU 厂商 A": "GPU A", "GPU 厂商 B": "GPU B", "CSP1": "CSP1",
27    "CSP2": "CSP2", "CSP3": "CSP3", "HBM 基础裸片": "HBM", "其他": "其他" };
28
29  const canvas = document.createElement("canvas");
30  canvas.style.cssText = "width:100%;height:420px;display:block;cursor:pointer";
31  canvas.setAttribute("data-drill-keep", "1");
32  body.appendChild(canvas);
33  const { fit, ctx } = U.bindCanvas(canvas);
34  let view = fit();
35  window.addEventListener("resize", () => { view = fit(); draw(true); });
36
37  const MONO = U.FONTS.mono, SANS = U.FONTS.sans;
38  // 一图一色系:GPU 阵营墨阶,ASIC 阵营电蓝一族,其余墨阶浅色(段 key 来自数据层,不可改)
39  const COL = { navy: "#051c2c", navyLo: "#42566a", blue: U.PAL.red, copper: U.PAL.redHi, purple: U.PAL.blueLo, grey: U.PAL.inkLo, greyLo: "#dbe2ea" };
40
41  // v8.6:删 2026F 一柱,只留 2025 与 2027F;份额标签约数化(~1/4 / ~1/3),精确值留 drill
42  const IDX = [0, 2];
43  const YEARS = IDX.map(i => D.years[i]);
44  const TOT = IDX.map(i => D.totals[i]);
45  const SHARE = IDX.map(i => D.derived.asic_share[i]);
46  const SHARE_TXT = ["~1/4", "~1/3"];
47
48  let hits = [], anim = 0, done = false;
49
50  function draw(skip) {
51    if (!done && !skip) { anim = U.clamp(anim + 0.018, 0, 1); if (anim >= 1) done = true; }
52    ctx.clearRect(0, 0, view.w, view.h);
53    hits = [];
54    const W = view.w;
55    const padL = 70, padR = 40;
56    const barW = W - padL - padR;
57
58    // ── 上半:三根组成堆叠横条 ──
59    ctx.font = "700 10.5px " + MONO; ctx.fillStyle = U.PAL.inkMd; ctx.textAlign = "left";
60    ctx.fillText("构成 · 占 TSMC AI 收入,%", padL, 26);
61
62    const barH = 34, gapY = 26, y0 = 44;
63    YEARS.forEach((yr, yi) => {
64      const y = y0 + yi * (barH + gapY);
65      const total = TOT[yi];
66      const reveal = U.clamp(anim * 3.2 - yi * 0.6, 0, 1);
67      ctx.font = "700 10.5px " + MONO; ctx.fillStyle = U.PAL.ink; ctx.textAlign = "left";
68      ctx.fillText(yr + (yr.includes("F") ? " · 估计" : ""), padL, y - 7);
69      let acc = 0;
70      D.segments.forEach(seg => {
71        const v = seg.vals[IDX[yi]];
72        if (v <= 0) return;
73        const w = (v / total) * barW * reveal;
74        ctx.fillStyle = COL[seg.color];
75        ctx.fillRect(padL + (acc / total) * barW, y, Math.max(0, w - 1), barH);
76        const segHit = { x: padL + (acc / total) * barW, y, w: Math.max(6, (v / total) * barW), h: barH,
77          drill: { title: NAME[seg.key] + " · " + yr, value: (v / total * 100).toFixed(1) + "% · 占 TSMC AI 收入(" + yr + ")",
78            sub: (seg.camp === "GPU" ? "GPU 阵营(厂商 A + B)" : seg.camp === "ASIC" ? "ASIC 阵营(CSP1–CSP3)" : "两大阵营之外") + " · 券商研究分析师估è
78®¡â€”—未经公司确认。",
79            source: D.source } };
80        hits.push(segHit);
81        if (reveal > 0.85 && (v / total) > 0.07) {
82          ctx.font = "700 9.5px " + MONO;
83          const lab = SHORT[NAME[seg.key]] + " " + (v / total * 100).toFixed(1) + "%";
84          if ((v / total) * barW >= ctx.measureText(lab).width + 10) {
85            ctx.fillStyle = seg.color === "navy" || seg.color === "navyLo" || seg.color === "blue" || seg.color === "copper" ? "#fff" : U.PAL.ink;
86            ctx.textAlign = "left";
87            ctx.fillText(lab, padL + (acc / total) * barW + 5, y + barH / 2 + 3);
88          }
89        }
90        acc += v;
91      });
92    });
93
94    // 阵营图例(全色板,匿名化)
95    const legY = y0 + YEARS.length * (barH + gapY) + 6;
96    const leg = [["GPU 厂商 A", COL.navy], ["GPU 厂商 B", COL.navyLo], ["CSP1", COL.blue], ["CSP2", COL.copper], ["CSP3", COL.purple], ["HBM/其他", COL.grey]];
97    let lx = padL;
98    leg.forEach(([t, c]) => {
99      ctx.fillStyle = c; ctx.fillRect(lx, legY - 8, 9, 9);
100      ctx.font = "9px " + MONO; ctx.fillStyle = U.PAL.inkMd; ctx.textAlign = "left";
101      ctx.fillText(t, lx + 12, legY);
102      lx += ctx.measureText(t).width + 26;
103    });
104    ctx.font = "9px " + MONO; ctx.fillStyle = U.PAL.inkLo;
105    ctx.fillText("柱条归一化至 100%;下方份额按未取整数值计算", padL, legY + 16);
106
107    // ── 下半:ASIC 份额 vs 50% 线 ──
108    const sy0 = legY + 54, sy1 = view.h - 56;
109    ctx.font = "700 10.5px " + MONO; ctx.fillStyle = U.PAL.inkMd; ctx.textAlign = "left";
110    ctx.fillText("ASIC 阵营占 TSMC AI 收入份额 · 对照 50% 反超线", padL, sy0 - 14);
111
112    const sX = v => padL + (v / 55) * barW;
113    // 50% 线
114    ctx.strokeStyle = U.PAL.neg; ctx.lineWidth = 1.4; ctx.setLineDash([5, 3]);
115    ctx.beginPath(); ctx.moveTo(sX(50), sy0); ctx.lineTo(sX(50), sy1); ctx.stroke();
116    ctx.setLineDash([]);
117    ctx.font = "700 9.5px " + MONO; ctx.fillStyle = U.PAL.neg; ctx.textAlign = "right";
118    ctx.fillText("50% ——并不存在的反超", sX(50) - 8, sy0 + 10);
119    // 网格
120    ctx.strokeStyle = "rgba(5,28,44,.08)"; ctx.lineWidth = 1;
121    [0, 10, 20, 30, 40].forEach(v => {
122      ctx.beginPath(); ctx.moveTo(sX(v), sy0); ctx.lineTo(sX(v), sy1); ctx.stroke();
123      ctx.font = "8.5px " + MONO; ctx.fillStyle = U.PAL.inkLo; ctx.textAlign = "center";
124      ctx.fillText(v + "%", sX(v), sy1 + 14);
125    });
126    // 份额折线(2025 → 2027F 两点)
127    ctx.strokeStyle = COL.blue; ctx.lineWidth = 2.2;
128    ctx.beginPath();
129    SHARE.forEach((v, i) => {
130      const px = sX(v * (skip ? 1 : U.clamp(anim * 2 - i * 0.3, 0.001, 1))), py = sy1 - 30;
131      i === 0 ? ctx.moveTo(sX(v * U.clamp(anim * 2, 0.001, 1)), py) : ctx.lineTo(px, py);
132    });
133    ctx.stroke();
134    SHARE.forEach((v, i) => {
135      const py = sy1 - 30;
136      const px = sX(v);
137      ctx.fillStyle = COL.blue;
138      ctx.beginPath(); ctx.arc(px, py, 5, 0, U.TAU); ctx.fill();
139      ctx.strokeStyle = "#fff"; ctx.lineWidth = 1.5; ctx.stroke();
140      ctx.font = "700 12px " + MONO; ctx.fillStyle = COL.blue; ctx.textAlign = "center";
141      ctx.fillText(SHARE_TXT[i], px, py - 12);
142      ctx.font = "9px " + MONO; ctx.fillStyle = U.PAL.inkLo;
143      ctx.fillText(YEARS[i], px, py + 18);
144      hits.push({ x: px - 22, y: py - 22, w: 44, h: 44,
145        drill: { title: "ASIC 阵营份额 · " + YEARS[i], value: SHARE_TXT[i] + "(" + v.toFixed(1) + "%) · 占 TSMC AI 收入",
146          sub: "GPU 阵营 " + D.derived.gpu_share[IDX[i]] + "%(其中 GPU 厂商 A 一家 " + D.derived.nvda_share[IDX[i]] + "%)。" + anon(D.derived.note),
147          source: D.source } });
148    });
149
150    // 落脚点行删除(v8.8:与 §9b 散文 "stays far from the 50% line" 及 A2 drill 的
151    // GPU ≈66% 重复;50% 线与 ~1/4 ~1/3 标注已自足)
152
153    if (!done && !skip) requestAnimationFrame(() => draw());
154  }
155
156  canvas.addEventListener("click", e => {
157    const r = canvas.getBoundingClientRect();
158    const x = e.clientX - r.left, y = e.clientY - r.top;
159    for (let i = hits.length - 1; i >= 0; i--) {
160      const h = hits[i];
161      if (x >= h.x && x <= h.x + h.w && y >= h.y && y <= h.y + h.h) {
162        U.showDrill({ ...h.drill, x: e.clientX, y: e.clientY });
163        return;
164      }
165    }
166  });
167
168  new IntersectionObserver(entries => {
169    for (const e of entries) if (e.isIntersecting && !anim) requestAnimationFrame(() => draw());
170  }, { threshold: 0.12 }).observe(host);
171  draw(true);
172})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.