如何在ECharts中精准复刻DCF估值柱状图UI(含堆叠区域与叠加标签)
Refining Your DCF Valuation Chart to Match Wall Street SVG Style
Great progress on your DCF chart! Let's tweak the details to nail that polished, professional look from the Wall Street SVG you referenced. Here are targeted improvements and the updated code to finish off the final 30% of visual fidelity:
Key Adjustments Made
- Tweaked Color Palette & Hatch Pattern: The red zone's hatch now uses a tighter, more subtle grid with better contrast, matching typical financial chart aesthetics. Green and yellow zones are adjusted for stronger visual hierarchy.
- Fixed Dynamic Positioning: Annotations (dashed line, arrow, price boxes) now automatically reposition when the window resizes, eliminating misalignment issues.
- Enhanced Annotation Styling: Price boxes get refined shadows, font weights, and a subtle border on the fair value box to mirror the reference SVG's depth.
- Added Subtle Gradient: The green zone uses a horizontal gradient to add dimensionality, replacing the flat color for a more polished feel.
Updated Full Code
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>DCF Valuation Chart</title> <style> body { margin:0; padding:40px; background:#0f1b2d; font-family: "Segoe UI", sans-serif; color:white; } .container { max-width:900px; margin:auto; } h1 { font-size:22px; font-weight:600; } .subtitle { color:#9ca3af; margin-top:8px; line-height:1.4; } .percent { margin-top:30px; font-size:40px; font-weight:800; color:#19e27c; } #chart { width:100%; height:420px; margin-top:40px; } .bottom-labels { display:flex; justify-content:space-between; margin-top:20px; font-weight:600; } .bottom-labels span { transform:rotate(-30deg); display:inline-block; } .green-label { color:#19e27c; } .yellow-label { color:#facc15; } .red-label { color:#ef4444; } </style> <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script> </head> <body> <div class="container"> <h1>1.1 Share Price vs Future Cash Flow Value</h1> <p class="subtitle"> What is the Fair Price when looking at future cash flows? For this estimate we use a Discounted Cash Flow model. </p> <div class="percent" id="percentText"></div> <div id="chart"></div> <div class="bottom-labels"> <span class="green-label">20% Undervalued</span> <span class="yellow-label">About Right</span> <span class="red-label">20% Overvalued</span> </div> </div> <script> document.addEventListener("DOMContentLoaded", function () { // Data const currentPrice = 293.19; const fairValue = 818.37; const maxValue = fairValue * 1.4; // % Calculation const percentUndervalued = ((fairValue - currentPrice) / fairValue * 100).toFixed(1); document.getElementById("percentText").innerText = percentUndervalued + "% Undervalued"; const chart = echarts.init(document.getElementById('chart')); // Helper to update annotations on resize function updateAnnotations() { const chartWidth = chart.getWidth(); const currentPriceX = (currentPrice / maxValue) * chartWidth; const fairValueX = (fairValue / maxValue) * chartWidth; chart.setOption({ graphic: [ // Thin green top line { type:'rect', left:0, top:55, shape:{ width:(fairValue/maxValue*100)+'%', height:4 }, style:{ fill:'#19e27c' } }, // Current price dashed line { type:'line', shape:{ x1:currentPriceX, y1:80, x2:currentPriceX, y2:300 }, style:{ stroke:'#ffffff', lineWidth:2, lineDash:[6,6] } }, // Arrow { type:'polygon', shape:{ points:[ [currentPriceX-6,75], [currentPriceX+6,75], [currentPriceX,60] ] }, style:{ fill:'#ffffff' } }, // Current price box { type:'group', left:currentPriceX - 65, top:'70%', children:[ { type:'rect', shape:{ width:130, height:80 }, style:{ fill:'#ffffff', shadowBlur:15, shadowColor:'rgba(0,0,0,0.6)', radius:6 } }, { type:'text', left:15, top:18, style:{ text:'Current Price', fill:'#374151', fontSize:13, fontWeight:500 } }, { type:'text', left:15, top:45, style:{ text:'US$'+currentPrice.toFixed(2), fill:'#111827', fontSize:20, fontWeight:'bold' } } ] }, // Fair value box { type:'group', left:fairValueX - 100, top:'65%', children:[ { type:'rect', shape:{ width:200, height:90 }, style:{ fill:'rgba(10,40,20,0.95)', shadowBlur:20, shadowColor:'rgba(0,0,0,0.7)', radius:8, borderColor:'rgba(25,226,124,0.3)', borderWidth:1 } }, { type:'text', left:18, top:25, style:{ text:'Cash Flow Value', fill:'#d1d5db', fontSize:14, fontWeight:500 } }, { type:'text', left:18, top:50, style:{ text:'US$'+fairValue.toFixed(2), fill:'#ffffff', fontSize:28, fontWeight:'bold' } } ] } ] }); } // Zone Sizes const greenZone = fairValue; const yellowZone = fairValue * 0.25; const redZone = maxValue - greenZone - yellowZone; const option = { animation:false, backgroundColor:'transparent', grid:{ left:0, right:0, top:60, bottom:120 }, xAxis:{ type:'value', min:0, max:maxValue, show:false }, yAxis:{ type:'category', data:[''], show:false }, series:[ { type:'bar', stack:'zones', data:[greenZone], barWidth:350, itemStyle:{ color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [ { offset: 0, color: '#178f55' }, { offset: 1, color: '#19e27c' } ]) } }, { type:'bar', stack:'zones', data:[yellowZone], barWidth:350, itemStyle:{ color:'#facc15' } }, { type:'bar', stack:'zones', data:[redZone], barWidth:350, itemStyle:{ color:{ type:'pattern', image:createHatchPattern('#991b1b','#7f1d1d'), repeat:'repeat' } } } ] }; chart.setOption(option); // Initial annotation setup + resize listener updateAnnotations(); window.addEventListener('resize', () => { chart.resize(); updateAnnotations(); }); // Hatch pattern generator function createHatchPattern(color1,color2){ const size=16; const canvas=document.createElement('canvas'); canvas.width=size; canvas.height=size; const ctx=canvas.getContext('2d'); ctx.fillStyle=color1; ctx.fillRect(0,0,size,size); ctx.strokeStyle=color2; ctx.lineWidth=1.5; ctx.beginPath(); ctx.moveTo(0,size); ctx.lineTo(size,0); ctx.moveTo(-size/2, size/2); ctx.lineTo(size/2, -size/2); ctx.stroke(); return canvas; } }); </script> </body> </html>
Quick Notes on the Changes
- The green gradient adds a subtle glossy effect that aligns with professional financial chart design.
- The red hatch pattern now uses a dual-line grid for a more authentic "overvalued" zone look.
- Annotation positions are dynamically recalculated on resize to ensure they stay locked to their respective data points.
- Text styling in the price boxes is tuned to match typical financial chart typography (lighter descriptive text, bolder value numbers).
内容的提问来源于stack exchange,提问作者Faizan Naeem
相关产品推荐
相关产品推荐

