-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtrajectory.html
More file actions
691 lines (616 loc) · 42.1 KB
/
trajectory.html
File metadata and controls
691 lines (616 loc) · 42.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>scRNA-seq 軌跡與擬時序分析 (Trajectory & Pseudotime Analysis)</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
/* Embedded CSS strictly for structural constraints, custom scrollbar, and chart containers */
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f8fafc; /* Tailwind slate-50 */
color: #334155; /* Tailwind slate-700 */
}
/* Custom Scrollbar */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: #f1f5f9; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
/* Chart Container Requirements */
.chart-container {
position: relative;
width: 100%;
max-width: 700px;
margin-left: auto;
margin-right: auto;
height: 45vh;
min-height: 350px;
max-height: 500px;
}
/* Animations & Utilities */
.fade-in { animation: fadeIn 0.5s ease-out forwards; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.tab-btn.active {
border-bottom-width: 2px;
border-color: #0d9488; /* teal-600 */
color: #0f766e; /* teal-700 */
font-weight: 600;
}
/* Dynamic dots for concept animation */
.sim-dot {
position: absolute;
width: 12px;
height: 12px;
border-radius: 50%;
transition: all 1.5s cubic-bezier(0.4, 0, 0.2, 1);
}
</style>
</head>
<body class="overflow-x-hidden antialiased flex flex-col min-h-screen">
<!-- Chosen Palette: Slate (Neutrals) + Teal (Primary/Biology) + Amber/Rose (Accents for branches/warnings) -->
<!-- Application Structure Plan:
The SPA uses a tabbed dashboard structure to prevent cognitive overload.
1. '核心概念' (Core Concepts): Interactive animation to explain the shift from static snapshots to dynamic pseudotime.
2. '分析流程' (Workflow): A vertical stepper that acts as an interactive list, revealing details on click.
3. '軌跡拓撲' (Topologies): The core interactive chart utilizing Chart.js to simulate different cellular trajectories, allowing users to visually compare linear, branching, circular, and tree structures.
4. '工具與下游' (Tools & Downstream): A grid-based comparative view of algorithms and a section highlighting downstream challenges using warning cards.
This structure is chosen because it transforms a linear text document into an exploratory learning tool, prioritizing user engagement and conceptual understanding over simple reading.
-->
<!-- Visualization & Content Choices:
1. Snapshot vs Movie -> Goal: Inform/Change -> Method: Custom JS/CSS DOM manipulation of div "dots" to visually show random cells aligning into a trajectory. Justification: Most intuitive way to explain "pseudotime". No SVG used.
2. Workflow -> Goal: Organize -> Method: Interactive Stepper (HTML/CSS/JS). Justification: Breaks down the 5 steps into digestible chunks.
3. Topologies -> Goal: Compare/Relationships -> Method: Chart.js Scatter Plot simulating cell coordinates. Justification: Dynamic, allows visual comparison of data shapes (Linear vs Branching). NO SVG used.
4. Algorithms -> Goal: Compare -> Method: CSS Grid cards with visual tags. Justification: Easy scanning of pros/cons.
-->
<!-- CONFIRMATION: NO SVG graphics used. NO Mermaid JS used. All visual elements are CSS, Unicode, or HTML5 Canvas (Chart.js). -->
<!-- Navigation Header -->
<nav class="bg-white shadow-sm sticky top-0 z-50 border-b border-slate-200">
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16 items-center">
<div class="flex items-center gap-2">
<span class="text-2xl text-teal-600">🧬</span>
<span class="font-bold text-xl text-slate-800 tracking-tight">scRNA-seq 軌跡分析</span>
</div>
<div class="hidden sm:flex space-x-8 h-full" id="nav-tabs">
<button class="tab-btn active px-3 py-2 text-sm font-medium text-slate-500 hover:text-teal-600 h-full flex items-center border-b-2 border-transparent transition-colors" data-target="section-concept">核心概念</button>
<button class="tab-btn px-3 py-2 text-sm font-medium text-slate-500 hover:text-teal-600 h-full flex items-center border-b-2 border-transparent transition-colors" data-target="section-workflow">標準流程</button>
<button class="tab-btn px-3 py-2 text-sm font-medium text-slate-500 hover:text-teal-600 h-full flex items-center border-b-2 border-transparent transition-colors" data-target="section-topology">軌跡拓撲</button>
<button class="tab-btn px-3 py-2 text-sm font-medium text-slate-500 hover:text-teal-600 h-full flex items-center border-b-2 border-transparent transition-colors" data-target="section-tools">工具與下游</button>
</div>
<!-- Mobile menu button (Simplified for SPA) -->
<div class="sm:hidden flex items-center">
<select id="mobile-nav" class="bg-slate-100 border border-slate-300 text-slate-700 rounded-md py-1 px-2 focus:outline-none focus:ring-2 focus:ring-teal-500">
<option value="section-concept">核心概念</option>
<option value="section-workflow">標準流程</option>
<option value="section-topology">軌跡拓撲</option>
<option value="section-tools">工具與下游</option>
</select>
</div>
</div>
</div>
</nav>
<!-- Main Content Container -->
<main class="flex-grow max-w-6xl mx-auto w-full px-4 sm:px-6 lg:px-8 py-8">
<!-- SECTION: Concept -->
<section id="section-concept" class="view-section fade-in block">
<div class="mb-8">
<h1 class="text-3xl sm:text-4xl font-extrabold text-slate-900 mb-4">從「靜態快照」到「動態演化」</h1>
<p class="text-lg text-slate-600 max-w-3xl">
在單細胞轉錄組測序(scRNA-seq)分析中,軌跡與擬時序分析是探索細胞動態演變過程的核心技術。此區塊解釋了如何從破壞性的測序數據中,重建細胞生命週期。
</p>
</div>
<div class="grid md:grid-cols-2 gap-8 items-stretch mb-8">
<div class="bg-white p-6 rounded-xl shadow-sm border border-slate-200">
<h2 class="text-xl font-bold text-teal-800 mb-3 flex items-center gap-2">📸 為什麼需要軌跡分析?</h2>
<p class="text-slate-600 mb-4 leading-relaxed">
在生物學實驗中,scRNA-seq 測序會裂解細胞,因此我們只能得到某個時間點的細胞狀態(<strong class="text-slate-800">靜態快照</strong>)。然而,在發育或分化過程中,細胞的狀態是連續變化的。
</p>
<div class="bg-teal-50 border-l-4 border-teal-500 p-4 rounded-r-md">
<p class="text-sm text-teal-900">
<strong>核心假設:</strong> 群體中不同細胞正處於分化路徑上的不同階段。透過比較細胞間基因表達的相似性,我們可以推斷出它們的演變路徑。
</p>
</div>
</div>
<div class="bg-white p-6 rounded-xl shadow-sm border border-slate-200">
<h2 class="text-xl font-bold text-teal-800 mb-3 flex items-center gap-2">⏱️ 什麼是擬時序 (Pseudotime)?</h2>
<p class="text-slate-600 mb-4 leading-relaxed">
擬時序並不是現實世界中的「秒」或「小時」,而是衡量細胞在某個生物過程(如分化)中<strong>進展程度</strong>的一個相對數值。
</p>
<ul class="space-y-3 mt-4 text-sm text-slate-700">
<li class="flex items-start gap-2">
<span class="text-teal-500 mt-0.5">▪</span>
<span>基於基因表達特徵計算出的抽象軸。</span>
</li>
<li class="flex items-start gap-2">
<span class="text-teal-500 mt-0.5">▪</span>
<span><strong>數值越小:</strong> 通常代表細胞越處於「起始狀態」(如幹細胞)。</span>
</li>
<li class="flex items-start gap-2">
<span class="text-teal-500 mt-0.5">▪</span>
<span><strong>數值越大:</strong> 代表越「成熟」或處於分化末端。</span>
</li>
</ul>
</div>
</div>
<!-- Interactive Concept Widget -->
<div class="bg-slate-900 rounded-xl p-6 sm:p-8 shadow-lg text-white overflow-hidden relative">
<div class="relative z-10 flex flex-col items-center">
<h3 class="text-2xl font-bold mb-2">概念互動:構建擬時序</h3>
<p class="text-slate-400 mb-6 text-center max-w-xl">點擊下方按鈕,觀察如何將隨機捕獲的靜態細胞(散亂分佈)依據基因相似性排列成連續的發展軌跡(擬時序)。</p>
<div class="w-full max-w-2xl h-48 bg-slate-800 rounded-lg border border-slate-700 relative mb-6 overflow-hidden" id="concept-canvas">
<!-- JS will populate dots here -->
<div id="pseudo-axis" class="absolute bottom-2 left-4 right-4 h-1 bg-gradient-to-r from-teal-400 to-rose-500 opacity-0 transition-opacity duration-1000"></div>
<div id="pseudo-label-start" class="absolute bottom-4 left-4 text-xs font-bold text-teal-400 opacity-0 transition-opacity duration-1000">起始狀態 (Root)</div>
<div id="pseudo-label-end" class="absolute bottom-4 right-4 text-xs font-bold text-rose-400 opacity-0 transition-opacity duration-1000">成熟狀態 (Terminal)</div>
</div>
<button id="toggle-concept-btn" class="bg-teal-600 hover:bg-teal-500 text-white px-6 py-3 rounded-full font-semibold transition-all transform active:scale-95 flex items-center gap-2 shadow-lg shadow-teal-500/30">
<span>▶</span> 執行軌跡推斷 (推演擬時序)
</button>
</div>
</div>
</section>
<!-- SECTION: Workflow -->
<section id="section-workflow" class="view-section hidden">
<div class="mb-8">
<h1 class="text-3xl sm:text-4xl font-extrabold text-slate-900 mb-4">標準工作流程</h1>
<p class="text-lg text-slate-600 max-w-3xl">
軌跡分析有一套標準的計算流程。點擊左側的步驟,在右側查看該步驟的技術細節與目標。
</p>
</div>
<div class="flex flex-col md:flex-row gap-6 bg-white p-6 rounded-xl shadow-sm border border-slate-200">
<!-- Stepper Navigation -->
<div class="md:w-1/3 flex flex-col border-l-2 border-slate-100 ml-3">
<button class="workflow-step-btn relative pl-6 py-4 text-left group focus:outline-none" data-step="1">
<span class="workflow-dot absolute -left-[9px] top-1/2 -translate-y-1/2 w-4 h-4 rounded-full border-4 border-white bg-teal-500 shadow transition-colors"></span>
<h3 class="font-bold text-teal-700 text-lg group-hover:text-teal-600 transition-colors">1. 降維</h3>
<p class="text-sm text-slate-500 hidden sm:block">Dimensionality Reduction</p>
</button>
<button class="workflow-step-btn relative pl-6 py-4 text-left group focus:outline-none" data-step="2">
<span class="workflow-dot absolute -left-[9px] top-1/2 -translate-y-1/2 w-4 h-4 rounded-full border-4 border-white bg-slate-300 group-hover:bg-teal-300 transition-colors"></span>
<h3 class="font-bold text-slate-600 text-lg group-hover:text-teal-600 transition-colors">2. 構建圖/曲線</h3>
<p class="text-sm text-slate-500 hidden sm:block">Trajectory Building</p>
</button>
<button class="workflow-step-btn relative pl-6 py-4 text-left group focus:outline-none" data-step="3">
<span class="workflow-dot absolute -left-[9px] top-1/2 -translate-y-1/2 w-4 h-4 rounded-full border-4 border-white bg-slate-300 group-hover:bg-teal-300 transition-colors"></span>
<h3 class="font-bold text-slate-600 text-lg group-hover:text-teal-600 transition-colors">3. 定義起點</h3>
<p class="text-sm text-slate-500 hidden sm:block">Root Selection</p>
</button>
<button class="workflow-step-btn relative pl-6 py-4 text-left group focus:outline-none" data-step="4">
<span class="workflow-dot absolute -left-[9px] top-1/2 -translate-y-1/2 w-4 h-4 rounded-full border-4 border-white bg-slate-300 group-hover:bg-teal-300 transition-colors"></span>
<h3 class="font-bold text-slate-600 text-lg group-hover:text-teal-600 transition-colors">4. 分配擬時序值</h3>
<p class="text-sm text-slate-500 hidden sm:block">Assigning Pseudotime</p>
</button>
<button class="workflow-step-btn relative pl-6 py-4 text-left group focus:outline-none" data-step="5">
<span class="workflow-dot absolute -left-[9px] top-1/2 -translate-y-1/2 w-4 h-4 rounded-full border-4 border-white bg-slate-300 group-hover:bg-teal-300 transition-colors"></span>
<h3 class="font-bold text-slate-600 text-lg group-hover:text-teal-600 transition-colors">5. 下游分析</h3>
<p class="text-sm text-slate-500 hidden sm:block">Downstream Analysis</p>
</button>
</div>
<!-- Stepper Content -->
<div class="md:w-2/3 bg-slate-50 rounded-lg p-6 flex flex-col justify-center min-h-[250px]" id="workflow-content-area">
<!-- Default content (Step 1) -->
<div class="fade-in">
<div class="inline-block bg-teal-100 text-teal-800 px-3 py-1 rounded-full text-xs font-bold mb-4 tracking-wider">STEP 1</div>
<h3 class="text-2xl font-bold text-slate-800 mb-3">降維 (Dimensionality Reduction)</h3>
<p class="text-slate-600 leading-relaxed text-lg">
將數萬個基因的表達矩陣投射到低維空間(如 UMAP, t-SNE 或 Diffusion Maps)。這一步非常關鍵,它能使基因表達特徵相似的細胞在二維或三維空間中聚集在一起,從而暴露出潛在的分化趨勢與物理結構。
</p>
</div>
</div>
</div>
</section>
<!-- SECTION: Topologies -->
<section id="section-topology" class="view-section hidden">
<div class="mb-6">
<h1 class="text-3xl sm:text-4xl font-extrabold text-slate-900 mb-4">常見的軌跡拓撲類型</h1>
<p class="text-lg text-slate-600 max-w-3xl">
不同的生物學過程對應不同的軌跡結構。點擊下方按鈕,在圖表中模擬觀察不同類型的細胞群體分佈拓撲。顏色從藍色到紅色代表擬時序(Pseudotime)的推進。
</p>
</div>
<div class="bg-white p-6 rounded-xl shadow-sm border border-slate-200">
<!-- Interaction Controls -->
<div class="flex flex-wrap gap-2 mb-6 justify-center">
<button class="topo-btn active bg-teal-600 text-white px-4 py-2 rounded-md font-medium text-sm transition-colors" data-topo="linear">線性 (Linear)</button>
<button class="topo-btn bg-slate-100 text-slate-700 hover:bg-slate-200 px-4 py-2 rounded-md font-medium text-sm transition-colors" data-topo="bifurcation">分叉 (Bifurcation)</button>
<button class="topo-btn bg-slate-100 text-slate-700 hover:bg-slate-200 px-4 py-2 rounded-md font-medium text-sm transition-colors" data-topo="circular">循環 (Circular)</button>
<button class="topo-btn bg-slate-100 text-slate-700 hover:bg-slate-200 px-4 py-2 rounded-md font-medium text-sm transition-colors" data-topo="tree">樹狀 (Tree)</button>
</div>
<!-- Dynamic Text Context -->
<div class="text-center mb-6 h-12 flex items-center justify-center">
<p id="topo-description" class="text-slate-700 font-medium">
<!-- Filled by JS -->
</p>
</div>
<!-- Chart Container (Strict styling applied via CSS class) -->
<div class="chart-container">
<canvas id="topologyChart"></canvas>
</div>
</div>
</section>
<!-- SECTION: Tools & Downstream -->
<section id="section-tools" class="view-section hidden">
<div class="mb-8">
<h1 class="text-3xl sm:text-4xl font-extrabold text-slate-900 mb-4">工具選擇與下游分析</h1>
<p class="text-lg text-slate-600 max-w-3xl">
軌跡構建只是起點。了解各類算法的適用場景,並深入進行基因動態分析與避開常見陷阱,才能挖掘出真正的生物學意義。
</p>
</div>
<div class="grid lg:grid-cols-2 gap-8">
<!-- Algorithms Grid -->
<div>
<h2 class="text-2xl font-bold text-slate-800 mb-4 flex items-center gap-2">🧰 主流分析工具與算法</h2>
<div class="grid grid-cols-1 gap-4">
<!-- Monocle3 -->
<div class="bg-white border border-slate-200 p-4 rounded-lg shadow-sm hover:shadow-md transition-shadow relative overflow-hidden">
<div class="absolute top-0 right-0 bg-teal-100 text-teal-800 text-xs font-bold px-2 py-1 rounded-bl-lg">最主流推薦</div>
<h3 class="font-bold text-lg text-slate-800 mb-1">Monocle3</h3>
<p class="text-sm text-slate-600 mb-2">基於學習主圖(Principal Graph),支持複雜分支與多個起點。</p>
<div class="text-xs bg-slate-50 p-2 rounded text-slate-700 border border-slate-100">
<strong>適用場景:</strong> 大規模數據與複雜分枝結構。
</div>
</div>
<!-- scVelo -->
<div class="bg-white border border-slate-200 border-l-4 border-l-rose-400 p-4 rounded-lg shadow-sm hover:shadow-md transition-shadow relative">
<div class="absolute top-2 right-2 text-xs font-bold text-rose-500 bg-rose-50 px-2 py-1 rounded">具備方向性</div>
<h3 class="font-bold text-lg text-slate-800 mb-1">scVelo / Velocyto</h3>
<p class="text-sm text-slate-600 mb-2"><strong>RNA 速率分析 (RNA Velocity)</strong>。利用未剪接與已剪接 mRNA 的比例推斷未來狀態。</p>
<div class="text-xs bg-slate-50 p-2 rounded text-slate-700 border border-slate-100">
<strong>優勢:</strong> 不需要手動指定起點即可預測演化趨勢向量。
</div>
</div>
<!-- Slingshot & Others -->
<div class="grid grid-cols-2 gap-4">
<div class="bg-white border border-slate-200 p-4 rounded-lg shadow-sm">
<h3 class="font-bold text-md text-slate-800 mb-1">Slingshot</h3>
<p class="text-xs text-slate-600 mb-2">基於聚類中心構建最小生成樹,擬合平滑曲線。</p>
<p class="text-xs text-teal-600 font-semibold">結果穩定,依賴聚類</p>
</div>
<div class="bg-white border border-slate-200 p-4 rounded-lg shadow-sm">
<h3 class="font-bold text-md text-slate-800 mb-1">PAGA</h3>
<p class="text-xs text-slate-600 mb-2">估計細胞群體間的連接強度,處理混合拓撲。</p>
<p class="text-xs text-teal-600 font-semibold">適合極大/極複雜數據</p>
</div>
</div>
<div class="bg-white border border-slate-200 p-4 rounded-lg shadow-sm">
<h3 class="font-bold text-md text-slate-800 mb-1">Diffusion Maps</h3>
<p class="text-xs text-slate-600">利用擴散距離捕捉細微連續變化。適合單一、線性的分化路徑。</p>
</div>
</div>
</div>
<!-- Downstream & Challenges -->
<div class="flex flex-col gap-6">
<!-- Downstream Analysis -->
<div class="bg-teal-800 text-white p-6 rounded-xl shadow-md">
<h2 class="text-xl font-bold mb-4 flex items-center gap-2">🔬 軌跡分析後的下游研究</h2>
<div class="space-y-4">
<div class="border-b border-teal-700 pb-3">
<h4 class="font-semibold text-teal-200 mb-1">A. 擬時序差異基因分析 (BEAM)</h4>
<p class="text-sm text-teal-50">尋找表達水平隨擬時序變化的動態基因。包含:早期驅動基因 (起始)、分支特異性基因 (命運選擇)、末端標誌基因 (成熟特徵)。</p>
</div>
<div class="border-b border-teal-700 pb-3">
<h4 class="font-semibold text-teal-200 mb-1">B. 分支點分析 (Branch Point Analysis)</h4>
<p class="text-sm text-teal-50">在分叉點之前,分析哪些基因的表達失衡導致了細胞選擇路徑 A 而非路徑 B,揭示命運決定機制。</p>
</div>
<div>
<h4 class="font-semibold text-teal-200 mb-1">C. 轉錄調控網絡推斷</h4>
<p class="text-sm text-teal-50">結合 TF(轉錄因子)分析(如 SCENIC),觀察調控網絡隨時間的動態切換。</p>
</div>
</div>
</div>
<!-- Challenges / Warnings -->
<div class="bg-amber-50 border border-amber-200 p-6 rounded-xl">
<h2 class="text-xl font-bold text-amber-900 mb-3 flex items-center gap-2">⚠️ 注意事項與挑戰</h2>
<ul class="space-y-2">
<li class="flex items-start gap-2 text-sm text-amber-800">
<span class="font-bold text-amber-600">▪</span>
<span><strong>過度解釋:</strong> 算法總能畫出一條路徑,但其生物學意義必須通過實驗(如 lineage tracing)驗證。</span>
</li>
<li class="flex items-start gap-2 text-sm text-amber-800">
<span class="font-bold text-amber-600">▪</span>
<span><strong>數據缺失 (Dropout):</strong> scRNA-seq 的噪音可能導致軌跡斷裂或產生虛假的分支。</span>
</li>
<li class="flex items-start gap-2 text-sm text-amber-800">
<span class="font-bold text-amber-600">▪</span>
<span><strong>密度偏差:</strong> 若中間過渡態細胞數量極少,算法可能無法將起點與終點正確連接。</span>
</li>
<li class="flex items-start gap-2 text-sm text-amber-800">
<span class="font-bold text-amber-600">▪</span>
<span><strong>根節點指定:</strong> 錯誤的起點會導致整個擬時序的生物學方向完全相反。</span>
</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<!-- App JavaScript Logic -->
<script>
document.addEventListener('DOMContentLoaded', () => {
// --- Navigation Logic ---
const tabBtns = document.querySelectorAll('.tab-btn');
const sections = document.querySelectorAll('.view-section');
const mobileNav = document.getElementById('mobile-nav');
function switchSection(targetId) {
// Update tabs
tabBtns.forEach(btn => {
if (btn.getAttribute('data-target') === targetId) {
btn.classList.add('active', 'border-teal-600', 'text-teal-700', 'font-semibold');
btn.classList.remove('border-transparent', 'text-slate-500');
} else {
btn.classList.remove('active', 'border-teal-600', 'text-teal-700', 'font-semibold');
btn.classList.add('border-transparent', 'text-slate-500');
}
});
// Update mobile select
mobileNav.value = targetId;
// Toggle sections with animation
sections.forEach(sec => {
if (sec.id === targetId) {
sec.classList.remove('hidden');
sec.classList.remove('fade-in');
void sec.offsetWidth; // Trigger reflow for animation restart
sec.classList.add('fade-in');
} else {
sec.classList.add('hidden');
}
});
}
tabBtns.forEach(btn => {
btn.addEventListener('click', () => switchSection(btn.getAttribute('data-target')));
});
mobileNav.addEventListener('change', (e) => switchSection(e.target.value));
// --- Module 1: Concept Animation Logic ---
const conceptCanvas = document.getElementById('concept-canvas');
const toggleConceptBtn = document.getElementById('toggle-concept-btn');
const pseudoAxis = document.getElementById('pseudo-axis');
const pseudoLabelStart = document.getElementById('pseudo-label-start');
const pseudoLabelEnd = document.getElementById('pseudo-label-end');
const numDots = 40;
const dots = [];
let isDynamic = false;
// Initialize dots randomly
for (let i = 0; i < numDots; i++) {
const dot = document.createElement('div');
dot.className = 'sim-dot bg-slate-400';
// Random starting positions (Static Snapshot)
const startX = Math.random() * 90 + 5; // 5% to 95%
const startY = Math.random() * 80 + 10;
dot.style.left = `${startX}%`;
dot.style.top = `${startY}%`;
// Store target positions for Dynamic (Pseudotime) state
// Sort by X conceptually
const targetX = (i / numDots) * 80 + 10;
// Add a little sine wave noise to make it look like a trajectory
const targetY = 50 + Math.sin(i * 0.2) * 15 + (Math.random() * 10 - 5);
dot.dataset.startX = startX;
dot.dataset.startY = startY;
dot.dataset.targetX = targetX;
dot.dataset.targetY = targetY;
// Assign color based on position in trajectory
const ratio = i / numDots;
// Simple color interpolation logic for the dots
const r = Math.floor(45 + ratio * (244 - 45)); // Teal to Rose
const g = Math.floor(212 - ratio * (212 - 63));
const b = Math.floor(191 - ratio * (191 - 94));
dot.dataset.color = `rgb(${r}, ${g}, ${b})`;
conceptCanvas.appendChild(dot);
dots.push(dot);
}
toggleConceptBtn.addEventListener('click', () => {
isDynamic = !isDynamic;
if (isDynamic) {
toggleConceptBtn.innerHTML = '<span>↺</span> 還原為靜態快照';
toggleConceptBtn.classList.replace('bg-teal-600', 'bg-slate-600');
toggleConceptBtn.classList.replace('hover:bg-teal-500', 'hover:bg-slate-500');
toggleConceptBtn.classList.replace('shadow-teal-500/30', 'shadow-slate-500/30');
pseudoAxis.style.opacity = '1';
pseudoLabelStart.style.opacity = '1';
pseudoLabelEnd.style.opacity = '1';
dots.forEach((dot) => {
dot.style.left = `${dot.dataset.targetX}%`;
dot.style.top = `${dot.dataset.targetY}%`;
dot.style.backgroundColor = dot.dataset.color;
});
} else {
toggleConceptBtn.innerHTML = '<span>▶</span> 執行軌跡推斷 (推演擬時序)';
toggleConceptBtn.classList.replace('bg-slate-600', 'bg-teal-600');
toggleConceptBtn.classList.replace('hover:bg-slate-500', 'hover:bg-teal-500');
toggleConceptBtn.classList.replace('shadow-slate-500/30', 'shadow-teal-500/30');
pseudoAxis.style.opacity = '0';
pseudoLabelStart.style.opacity = '0';
pseudoLabelEnd.style.opacity = '0';
dots.forEach((dot) => {
dot.style.left = `${dot.dataset.startX}%`;
dot.style.top = `${dot.dataset.startY}%`;
dot.style.backgroundColor = '#94a3b8'; // slate-400
});
}
});
// --- Module 2: Workflow Stepper Logic ---
const workflowData = {
"1": { title: "降維 (Dimensionality Reduction)", content: "將數萬個基因的表達矩陣投射到低維空間(如 UMAP, t-SNE 或 Diffusion Maps)。這一步非常關鍵,它能使基因表達特徵相似的細胞在空間中聚集在一起,從而暴露出潛在的分化趨勢與物理結構。" },
"2": { title: "構建圖/曲線 (Trajectory Building)", content: "使用圖論算法(如最小生成樹 MST)或主曲線(Principal Curves)算法,連接降維空間中的各個細胞群集,形成一條或多條代表分化可能性的骨架「路徑」。" },
"3": { title: "定義起點 (Root Selection)", content: "算法純靠數學無法自動判斷哪一端才是真正的生物學起點。需要研究者根據已知的生物學背景知識(例如:幹細胞標誌基因的高表達區域),手動指定網絡中的「Root」節點。" },
"4": { title: "分配擬時序值 (Assigning Pseudotime)", content: "確定起點後,算法計算每個細胞沿著構建好的拓撲骨架路徑到達起點的幾何距離。距離起點越短,分配的擬時序值越小(處於早期);距離越長,數值越大(處於成熟期)。" },
"5": { title: "下游分析 (Downstream Analysis)", content: "獲取擬時序後,可以開始尋找隨擬時序變化的動態基因群(Time-series genes),分析細胞命運選擇的分支點(Branch points),並推斷轉錄因子(TF)的調控網絡變化。" }
};
const workflowBtns = document.querySelectorAll('.workflow-step-btn');
const workflowContentArea = document.getElementById('workflow-content-area');
workflowBtns.forEach(btn => {
btn.addEventListener('click', () => {
const step = btn.getAttribute('data-step');
// Reset styling
workflowBtns.forEach(b => {
const h3 = b.querySelector('h3');
const dot = b.querySelector('.workflow-dot');
h3.classList.remove('text-teal-700');
h3.classList.add('text-slate-600');
dot.classList.remove('bg-teal-500');
dot.classList.add('bg-slate-300');
});
// Active styling
btn.querySelector('h3').classList.add('text-teal-700');
btn.querySelector('h3').classList.remove('text-slate-600');
btn.querySelector('.workflow-dot').classList.add('bg-teal-500');
btn.querySelector('.workflow-dot').classList.remove('bg-slate-300');
// Inject content
workflowContentArea.innerHTML = `
<div class="fade-in">
<div class="inline-block bg-teal-100 text-teal-800 px-3 py-1 rounded-full text-xs font-bold mb-4 tracking-wider">STEP ${step}</div>
<h3 class="text-2xl font-bold text-slate-800 mb-3">${workflowData[step].title}</h3>
<p class="text-slate-600 leading-relaxed text-lg">${workflowData[step].content}</p>
</div>
`;
});
});
// --- Module 3: Topologies Chart Logic ---
const ctx = document.getElementById('topologyChart').getContext('2d');
let topoChart = null;
// Topology Data Generators (Adding noise to mathematical shapes)
// Points are objects {x, y, pseudo} where pseudo defines the color gradient
function generateTopologyData(type) {
const data = [];
const addNoise = (val, amount) => val + (Math.random() - 0.5) * amount;
if (type === 'linear') {
for (let i = 0; i < 200; i++) {
const t = i / 200;
data.push({ x: t * 10, y: addNoise(5, 1.5), pseudo: t });
}
}
else if (type === 'bifurcation') {
// Trunk
for (let i = 0; i < 100; i++) {
const t = i / 100;
data.push({ x: t * 4, y: addNoise(5, 1), pseudo: t * 0.4 });
}
// Branch A (Up)
for (let i = 0; i < 75; i++) {
const t = i / 75;
data.push({ x: 4 + t * 5, y: 5 + t * 3 + addNoise(0, 1), pseudo: 0.4 + t * 0.6 });
}
// Branch B (Down)
for (let i = 0; i < 75; i++) {
const t = i / 75;
data.push({ x: 4 + t * 5, y: 5 - t * 3 + addNoise(0, 1), pseudo: 0.4 + t * 0.6 });
}
}
else if (type === 'circular') {
for (let i = 0; i < 250; i++) {
const angle = Math.random() * Math.PI * 2;
const r = 4 + addNoise(0, 1);
// Map angle 0-2PI to pseudotime 0-1
const pseudo = angle / (Math.PI * 2);
data.push({ x: 5 + r * Math.cos(angle), y: 5 + r * Math.sin(angle), pseudo: pseudo });
}
}
else if (type === 'tree') {
// Trunk
for (let i = 0; i < 60; i++) {
const t = i / 60;
data.push({ x: t * 3, y: addNoise(5, 0.8), pseudo: t * 0.3 });
}
// Main Branch 1
for (let i = 0; i < 50; i++) {
const t = i / 50;
data.push({ x: 3 + t * 3, y: 5 + t * 2 + addNoise(0, 0.8), pseudo: 0.3 + t * 0.3 });
}
// Main Branch 2
for (let i = 0; i < 50; i++) {
const t = i / 50;
data.push({ x: 3 + t * 4, y: 5 - t * 3 + addNoise(0, 0.8), pseudo: 0.3 + t * 0.4 });
}
// Sub Branch 1A
for (let i = 0; i < 40; i++) {
const t = i / 40;
data.push({ x: 6 + t * 3, y: 7 + t * 1 + addNoise(0, 0.6), pseudo: 0.6 + t * 0.4 });
}
// Sub Branch 1B
for (let i = 0; i < 40; i++) {
const t = i / 40;
data.push({ x: 6 + t * 3, y: 7 - t * 1 + addNoise(0, 0.6), pseudo: 0.6 + t * 0.4 });
}
}
return data;
}
const topoDescriptions = {
'linear': '<strong>線性 (Linear):</strong> 細胞從狀態 A 直接演變為狀態 B(例如:前體細胞 → 成熟細胞)。',
'bifurcation': '<strong>分叉 (Bifurcation):</strong> 細胞面臨「命運選擇」,分化為兩種不同的細胞類型(如:造血幹細胞分化為髓系或淋巴系)。',
'circular': '<strong>循環 (Circular):</strong> 描述細胞週期(Cell Cycle)或具週期性特徵的生理過程。狀態會回到原點。',
'tree': '<strong>樹狀/複雜圖 (Tree):</strong> 包含多個分支與交叉,適用於複雜組織發育研究,展示多種終端分化。'
};
function updateChart(type) {
const rawData = generateTopologyData(type);
// Color mapping function based on pseudotime value (0 to 1)
// Using d3.interpolateTurbo roughly (Blue -> Green -> Yellow -> Red)
const getColor = (t) => {
const r = Math.max(0, Math.min(255, Math.floor(255 * (1.5 - Math.abs(1 - 4 * (t - 0.5))))));
const g = Math.max(0, Math.min(255, Math.floor(255 * (1.5 - Math.abs(1 - 4 * (t - 0.25))))));
const b = Math.max(0, Math.min(255, Math.floor(255 * (1.5 - Math.abs(1 - 4 * t)))));
return `rgba(${r}, ${g}, ${b}, 0.7)`;
};
const pointColors = rawData.map(d => getColor(d.pseudo));
if (topoChart) {
topoChart.destroy();
}
topoChart = new Chart(ctx, {
type: 'scatter',
data: {
datasets: [{
label: '細胞狀態點',
data: rawData,
backgroundColor: pointColors,
borderColor: 'transparent',
pointRadius: 4,
pointHoverRadius: 7
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 800,
easing: 'easeOutQuart'
},
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: (context) => {
const p = context.raw.pseudo.toFixed(2);
return `擬時序值: ${p}`;
}
}
}
},
scales: {
x: { display: false, min: -1, max: 11 },
y: { display: false, min: 0, max: 10 }
}
}
});
document.getElementById('topo-description').innerHTML = topoDescriptions[type];
}
// Initial Chart Render
updateChart('linear');
// Topology Buttons Logic
const topoBtns = document.querySelectorAll('.topo-btn');
topoBtns.forEach(btn => {
btn.addEventListener('click', (e) => {
topoBtns.forEach(b => {
b.classList.remove('bg-teal-600', 'text-white', 'active');
b.classList.add('bg-slate-100', 'text-slate-700');
});
btn.classList.add('bg-teal-600', 'text-white', 'active');
btn.classList.remove('bg-slate-100', 'text-slate-700');
updateChart(btn.getAttribute('data-topo'));
});
});
});
</script>
</body>
</html>