| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>288度环形进度条</title>
- <style>
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- }
-
- body {
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
- background: linear-gradient(135deg, #1a1a2e, #16213e, #0f3460);
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 20px;
- color: #fff;
- }
-
- .container {
- max-width: 900px;
- width: 100%;
- text-align: center;
- }
-
- h1 {
- margin-bottom: 10px;
- color: #fff;
- font-size: 2.5rem;
- text-shadow: 0 2px 10px rgba(255, 255, 255, 0.2);
- background: linear-gradient(90deg, #FFC900, #13A6FF, #806EFF, #FF8EEC, #FE4F51);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- }
-
- .subtitle {
- color: rgba(255, 255, 255, 0.8);
- margin-bottom: 30px;
- font-size: 1.1rem;
- }
-
- .info-section {
- background-color: rgba(255, 255, 255, 0.08);
- border-radius: 15px;
- padding: 20px;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
- backdrop-filter: blur(10px);
- border: 1px solid rgba(255, 255, 255, 0.1);
- margin-bottom: 25px;
- }
-
- .info-title {
- color: #4cd964;
- font-size: 1.3rem;
- margin-bottom: 15px;
- text-align: center;
- }
-
- .info-description {
- color: rgba(255, 255, 255, 0.9);
- text-align: left;
- line-height: 1.6;
- }
-
- .angle-info {
- display: flex;
- justify-content: space-around;
- margin-top: 20px;
- flex-wrap: wrap;
- gap: 15px;
- }
-
- .angle-item {
- text-align: center;
- padding: 15px;
- background: rgba(255, 255, 255, 0.05);
- border-radius: 8px;
- min-width: 150px;
- }
-
- .angle-value {
- font-size: 1.8rem;
- font-weight: bold;
- color: #FFC900;
- margin-bottom: 5px;
- }
-
- .angle-label {
- font-size: 0.9rem;
- color: rgba(255, 255, 255, 0.7);
- }
-
- .progress-container {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 20px;
- margin-bottom: 40px;
- }
-
- .progress-wrapper {
- background-color: rgba(255, 255, 255, 0.08);
- border-radius: 15px;
- padding: 30px;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
- transition: transform 0.3s ease;
- backdrop-filter: blur(10px);
- border: 1px solid rgba(255, 255, 255, 0.1);
- width: 100%;
- max-width: 500px;
- /* 关键修改:向左旋转90度 */
- transform: rotate(-90deg);
- }
-
- .progress-wrapper:hover {
- transform: rotate(-90deg) translateY(-5px);
- }
-
- canvas {
- display: block;
- margin: 0 auto;
- }
-
- .progress-info {
- margin-top: 20px;
- text-align: center;
- /* 需要反向旋转,让文字保持正常方向 */
- transform: rotate(90deg);
- }
-
- .progress-percent {
- font-size: 2.5rem;
- font-weight: bold;
- text-shadow: 0 0 20px rgba(255, 255, 255, 0.2);
- color: #FFC900;
- }
-
- .progress-label {
- font-size: 1.1rem;
- color: rgba(255, 255, 255, 0.7);
- margin-top: 5px;
- }
-
- .current-color {
- margin-top: 10px;
- font-size: 1.2rem;
- color: rgba(255, 255, 255, 0.9);
- }
-
- .color-indicator {
- display: inline-block;
- width: 20px;
- height: 20px;
- border-radius: 50%;
- margin-right: 8px;
- vertical-align: middle;
- border: 2px solid rgba(255, 255, 255, 0.3);
- box-shadow: 0 0 10px currentColor;
- }
-
- .controls {
- background-color: rgba(255, 255, 255, 0.08);
- border-radius: 15px;
- padding: 30px;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
- margin-bottom: 30px;
- backdrop-filter: blur(10px);
- border: 1px solid rgba(255, 255, 255, 0.1);
- width: 100%;
- }
-
- .slider-container {
- margin-bottom: 20px;
- text-align: left;
- }
-
- label {
- display: block;
- margin-bottom: 8px;
- font-weight: 600;
- color: rgba(255, 255, 255, 0.9);
- }
-
- input[type="range"] {
- width: 100%;
- height: 8px;
- border-radius: 4px;
- background: rgba(255, 255, 255, 0.1);
- outline: none;
- -webkit-appearance: none;
- }
-
- input[type="range"]::-webkit-slider-thumb {
- -webkit-appearance: none;
- width: 22px;
- height: 22px;
- border-radius: 50%;
- background: linear-gradient(45deg, #FFC900, #FE4F51);
- cursor: pointer;
- box-shadow: 0 3px 10px rgba(0, 0, 0, 0.5);
- border: 2px solid rgba(255, 255, 255, 0.8);
- }
-
- .value-display {
- display: flex;
- justify-content: space-between;
- margin-top: 5px;
- font-size: 0.9rem;
- color: rgba(255, 255, 255, 0.7);
- }
-
- .button-container {
- display: flex;
- gap: 15px;
- justify-content: center;
- margin-top: 25px;
- flex-wrap: wrap;
- }
-
- button {
- padding: 12px 25px;
- background: linear-gradient(90deg, #FFC900, #FE4F51);
- color: white;
- border: none;
- border-radius: 8px;
- font-size: 1rem;
- cursor: pointer;
- transition: all 0.3s ease;
- box-shadow: 0 4px 15px rgba(255, 201, 0, 0.3);
- font-weight: 600;
- min-width: 120px;
- }
-
- button:hover {
- transform: translateY(-2px);
- box-shadow: 0 6px 20px rgba(255, 201, 0, 0.5);
- }
-
- button:active {
- transform: translateY(0);
- }
-
- .test-buttons {
- display: flex;
- gap: 10px;
- justify-content: center;
- margin-top: 20px;
- flex-wrap: wrap;
- }
-
- .test-btn {
- padding: 8px 16px;
- background: rgba(255, 255, 255, 0.1);
- color: white;
- border: 1px solid rgba(255, 255, 255, 0.2);
- border-radius: 6px;
- font-size: 0.9rem;
- cursor: pointer;
- transition: all 0.2s ease;
- }
-
- .test-btn:hover {
- background: rgba(255, 255, 255, 0.2);
- transform: translateY(-1px);
- }
-
- .visual-guide {
- display: flex;
- gap: 20px;
- margin: 20px 0;
- padding: 15px;
- background: rgba(0, 0, 0, 0.2);
- border-radius: 10px;
- justify-content: center;
- flex-wrap: wrap;
- align-items: center;
- }
-
- .guide-item {
- text-align: center;
- padding: 10px;
- }
-
- .guide-canvas {
- background: rgba(0, 0, 0, 0.2);
- border-radius: 10px;
- margin: 0 auto 10px;
- }
-
- .guide-label {
- font-size: 0.9rem;
- color: rgba(255, 255, 255, 0.8);
- }
-
- footer {
- margin-top: 40px;
- color: rgba(255, 255, 255, 0.6);
- font-size: 0.9rem;
- text-align: center;
- }
-
- .angle-display {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-top: 15px;
- padding: 10px;
- background: rgba(0, 0, 0, 0.2);
- border-radius: 8px;
- /* 需要反向旋转,让文字保持正常方向 */
- transform: rotate(90deg);
- }
-
- .angle-display-item {
- text-align: center;
- flex: 1;
- }
-
- .angle-display-value {
- font-size: 1.2rem;
- font-weight: bold;
- color: #FFC900;
- }
-
- .angle-display-label {
- font-size: 0.8rem;
- color: rgba(255, 255, 255, 0.7);
- }
-
- @media (max-width: 768px) {
- .progress-wrapper {
- max-width: 100%;
- }
-
- h1 {
- font-size: 2rem;
- }
-
- .button-container {
- flex-direction: column;
- }
-
- button {
- min-width: 100%;
- }
-
- .visual-guide {
- flex-direction: column;
- gap: 15px;
- }
- }
- </style>
- </head>
- <body>
- <div class="container">
- <h1>对称288度环形进度条</h1>
- <p class="subtitle">进度条从-144度(7点钟方向)开始,到144度(5点钟方向)结束</p>
-
- <div class="info-section">
- <div class="info-title">角度配置说明</div>
- <div class="info-description">
- <p><strong>总角度范围</strong>:-144° 到 144°(总计288°,占360°的80%)</p>
- <p><strong>起始位置</strong>:-144°(相对于12点钟方向,位于7点钟方向)</p>
- <p><strong>结束位置</strong>:144°(相对于12点钟方向,位于5点钟方向)</p>
- <p><strong>空白区域</strong>:144° 到 216°(右侧72°空白)</p>
- <p><strong>角度计算公式</strong>:角度 = -144° + 进度百分比 × 288° / 100</p>
- <p><strong>注意</strong>:整个圆环已向左旋转90度,使0°基准位于12点钟方向</p>
- </div>
-
- <div class="angle-info">
- <div class="angle-item">
- <div class="angle-value">-144°</div>
- <div class="angle-label">起始角度</div>
- <div class="angle-label">7点钟方向</div>
- </div>
- <div class="angle-item">
- <div class="angle-value">-90°</div>
- <div class="angle-label">左中角度</div>
- <div class="angle-label">9点钟方向</div>
- </div>
- <div class="angle-item">
- <div class="angle-value">0°</div>
- <div class="angle-label">中间角度</div>
- <div class="angle-label">12点钟方向</div>
- </div>
- <div class="angle-item">
- <div class="angle-value">90°</div>
- <div class="angle-label">右中角度</div>
- <div class="angle-label">3点钟方向</div>
- </div>
- <div class="angle-item">
- <div class="angle-value">144°</div>
- <div class="angle-label">结束角度</div>
- <div class="angle-label">5点钟方向</div>
- </div>
- </div>
- </div>
-
- <div class="visual-guide">
- <div class="guide-item">
- <canvas id="guideCanvas1" width="120" height="120" class="guide-canvas"></canvas>
- <div class="guide-label">完整圆</div>
- <div class="guide-label">360°</div>
- </div>
- <div class="guide-item">
- <div style="font-size: 2rem; color: #FFC900;">→</div>
- <div class="guide-label">转换为</div>
- </div>
- <div class="guide-item">
- <canvas id="guideCanvas2" width="120" height="120" class="guide-canvas"></canvas>
- <div class="guide-label">-144°到144°</div>
- <div class="guide-label">288°进度条</div>
- </div>
- </div>
-
- <div class="progress-container">
- <div class="progress-wrapper">
- <canvas id="progressCanvas" width="300" height="300"></canvas>
- <div class="progress-info">
- <div class="progress-percent" id="percentText">0%</div>
- <div class="progress-label">当前进度</div>
- <div class="current-color">
- <span id="colorIndicator" class="color-indicator" style="background-color: transparent;"></span>
- <span id="currentColorText">无进度</span>
- </div>
- </div>
-
- <div class="angle-display">
- <div class="angle-display-item">
- <div class="angle-display-value" id="startAngle">-144°</div>
- <div class="angle-display-label">起始角度 (7点)</div>
- </div>
- <div class="angle-display-item">
- <div class="angle-display-value" id="currentAngle">0°</div>
- <div class="angle-display-label">当前角度</div>
- </div>
- <div class="angle-display-item">
- <div class="angle-display-value" id="endAngle">144°</div>
- <div class="angle-display-label">结束角度 (5点)</div>
- </div>
- </div>
- </div>
- </div>
-
- <div class="controls">
- <div class="slider-container">
- <label for="progressSlider">进度控制:<span id="currentProgress">0</span>%</label>
- <input type="range" id="progressSlider" min="0" max="100" value="0" step="1">
- <div class="value-display">
- <span>0% (-144°)</span>
- <span>100% (144°)</span>
- </div>
- </div>
-
- <div class="test-buttons">
- <button class="test-btn" data-value="0">测试0% (-144°)</button>
- <button class="test-btn" data-value="25">测试25% (-72°)</button>
- <button class="test-btn" data-value="50">测试50% (0°)</button>
- <button class="test-btn" data-value="75">测试75% (72°)</button>
- <button class="test-btn" data-value="100">测试100% (144°)</button>
- </div>
-
- <div class="button-container">
- <button id="animateBtn">动画演示</button>
- <button id="resetBtn">重置到0%</button>
- <button id="toEndBtn">跳转到144°</button>
- <button id="randomBtn">随机进度</button>
- </div>
- </div>
-
- <footer>
- <p>对称288度环形进度条 | 圆环已旋转,0°基准位于12点钟方向</p>
- </footer>
- </div>
- <script>
- // 获取Canvas元素和上下文
- const canvas = document.getElementById('progressCanvas');
- const ctx = canvas.getContext('2d');
-
- // 获取指南Canvas
- const guideCanvas1 = document.getElementById('guideCanvas1');
- const guideCtx1 = guideCanvas1.getContext('2d');
- const guideCanvas2 = document.getElementById('guideCanvas2');
- const guideCtx2 = guideCanvas2.getContext('2d');
-
- // 颜色节点配置数组(按照百分比位置)
- const colorStops = [
- {percent: 0, color: '#FFC900'},
- {percent: 11, color: '#FFC900'},
- {percent: 32, color: '#13A6FF'},
- {percent: 50, color: '#806EFF'},
- {percent: 66, color: '#FF8EEC'},
- {percent: 89, color: '#FE4F51'},
- {percent: 100, color: '#FE4F51'}
- ];
-
- // 进度条相关变量
- let currentProgress = 0;
- let targetProgress = 0;
- let animationId = null;
- let radius = 120; // 圆环半径
- let thickness = 15; // 圆环厚度
-
- // 注意:由于整个圆环已经旋转了-90度,所以这里不需要修改角度计算
- // 原有的角度计算逻辑保持不变,CSS旋转会处理0度位置的调整
- let startAngleDeg = -144; // 起始角度:-144度(相对于原始坐标系)
- let endAngleDeg = 144; // 结束角度:144度(相对于原始坐标系)
- let totalAngleDeg = endAngleDeg - startAngleDeg; // 总角度:288度
-
- // 转换为弧度
- let startAngleRad = startAngleDeg * Math.PI / 180;
- let endAngleRad = endAngleDeg * Math.PI / 180;
- let totalAngleRad = totalAngleDeg * Math.PI / 180;
-
- // 空白区域角度(上方)
- let blankStartDeg = endAngleDeg;
- let blankEndDeg = startAngleDeg + 360;
- let blankAngleDeg = blankEndDeg - blankStartDeg; // 72度空白
-
- // 获取DOM元素
- const progressSlider = document.getElementById('progressSlider');
- const currentProgressSpan = document.getElementById('currentProgress');
- const percentText = document.getElementById('percentText');
- const currentColorText = document.getElementById('currentColorText');
- const colorIndicator = document.getElementById('colorIndicator');
- const currentAngle = document.getElementById('currentAngle');
- const startAngleDisplay = document.getElementById('startAngle');
- const endAngleDisplay = document.getElementById('endAngle');
- const animateBtn = document.getElementById('animateBtn');
- const resetBtn = document.getElementById('resetBtn');
- const toEndBtn = document.getElementById('toEndBtn');
- const randomBtn = document.getElementById('randomBtn');
- const testButtons = document.querySelectorAll('.test-btn');
-
- // 计算画布中心点
- const centerX = canvas.width / 2;
- const centerY = canvas.height / 2;
-
- // 指南Canvas中心点
- const guideCenterX = guideCanvas1.width / 2;
- const guideCenterY = guideCanvas1.height / 2;
- const guideRadius = 45;
-
- // 绘制完整圆指南
- function drawFullCircleGuide() {
- guideCtx1.clearRect(0, 0, guideCanvas1.width, guideCanvas1.height);
-
- // 绘制完整圆
- guideCtx1.beginPath();
- guideCtx1.arc(guideCenterX, guideCenterY, guideRadius, 0, Math.PI * 2);
- guideCtx1.lineWidth = 8;
- guideCtx1.strokeStyle = 'rgba(255, 255, 255, 0.1)';
- guideCtx1.stroke();
-
- // 绘制-144°和144°标记
- const angle1 = -144 * Math.PI / 180;
- const angle2 = 144 * Math.PI / 180;
-
- // 标记-144°位置(左下)
- const marker1X = guideCenterX + Math.cos(angle1) * guideRadius;
- const marker1Y = guideCenterY + Math.sin(angle1) * guideRadius;
- guideCtx1.beginPath();
- guideCtx1.arc(marker1X, marker1Y, 3, 0, Math.PI * 2);
- guideCtx1.fillStyle = '#FFC900';
- guideCtx1.fill();
-
- // 标记144°位置(右下)
- const marker2X = guideCenterX + Math.cos(angle2) * guideRadius;
- const marker2Y = guideCenterY + Math.sin(angle2) * guideRadius;
- guideCtx1.beginPath();
- guideCtx1.arc(marker2X, marker2Y, 3, 0, Math.PI * 2);
- guideCtx1.fillStyle = '#FE4F51';
- guideCtx1.fill();
-
- guideCtx1.font = '10px Arial';
- guideCtx1.textAlign = 'center';
- guideCtx1.fillStyle = 'white';
- guideCtx1.fillText('360°', guideCenterX, guideCenterY + 25);
- }
-
- // 绘制-144°到144°进度条指南
- function drawSymmetricCircleGuide() {
- guideCtx2.clearRect(0, 0, guideCanvas2.width, guideCanvas2.height);
-
- // 绘制完整背景圆
- guideCtx2.beginPath();
- guideCtx2.arc(guideCenterX, guideCenterY, guideRadius, 0, Math.PI * 2);
- guideCtx2.lineWidth = 8;
- guideCtx2.strokeStyle = 'rgba(255, 255, 255, 0.1)';
- guideCtx2.stroke();
-
- // 绘制-144°到144°进度条
- const guideStartAngle = startAngleDeg * Math.PI / 180; // -144度
- const guideEndAngle = endAngleDeg * Math.PI / 180; // 144度
-
- // 绘制进度条
- guideCtx2.beginPath();
- guideCtx2.arc(guideCenterX, guideCenterY, guideRadius, guideStartAngle, guideEndAngle);
- guideCtx2.lineWidth = 8;
- guideCtx2.lineCap = 'round';
- guideCtx2.strokeStyle = '#FFC900';
- guideCtx2.stroke();
-
- // 标记-144°位置(起点,左下)
- const marker1X = guideCenterX + Math.cos(guideStartAngle) * guideRadius;
- const marker1Y = guideCenterY + Math.sin(guideStartAngle) * guideRadius;
- guideCtx2.beginPath();
- guideCtx2.arc(marker1X, marker1Y, 3, 0, Math.PI * 2);
- guideCtx2.fillStyle = '#FFC900';
- guideCtx2.fill();
-
- // 标记144°位置(终点,右下)
- const marker2X = guideCenterX + Math.cos(guideEndAngle) * guideRadius;
- const marker2Y = guideCenterY + Math.sin(guideEndAngle) * guideRadius;
- guideCtx2.beginPath();
- guideCtx2.arc(marker2X, marker2Y, 3, 0, Math.PI * 2);
- guideCtx2.fillStyle = '#FE4F51';
- guideCtx2.fill();
-
- // 标记空白区域(144°到-144°,经过上方)
- guideCtx2.beginPath();
- guideCtx2.arc(guideCenterX, guideCenterY, guideRadius - 15, guideEndAngle, guideStartAngle + Math.PI * 2);
- guideCtx2.lineWidth = 2;
- guideCtx2.strokeStyle = 'rgba(255, 255, 255, 0.3)';
- guideCtx2.setLineDash([3, 3]);
- guideCtx2.stroke();
- guideCtx2.setLineDash([]);
-
- guideCtx2.font = '10px Arial';
- guideCtx2.textAlign = 'center';
- guideCtx2.fillStyle = 'white';
- guideCtx2.fillText('-144° to 144°', guideCenterX, guideCenterY + 25);
- }
-
- // 根据百分比获取颜色
- function getColorForPercent(percent) {
- // 确保百分比在0-100范围内
- percent = Math.max(0, Math.min(100, percent));
-
- // 如果进度为0,返回null表示无颜色
- if (percent === 0) {
- return null;
- }
-
- // 找到百分比所在的两个颜色节点
- for (let i = 0; i < colorStops.length - 1; i++) {
- const startStop = colorStops[i];
- const endStop = colorStops[i + 1];
-
- if (percent >= startStop.percent && percent <= endStop.percent) {
- // 计算在两个节点之间的比例
- const t = (percent - startStop.percent) / (endStop.percent - startStop.percent);
- return interpolateColor(startStop.color, endStop.color, t);
- }
- }
-
- return colorStops[colorStops.length - 1].color;
- }
-
- // 颜色插值函数
- function interpolateColor(color1, color2, t) {
- // 将十六进制颜色转换为RGB
- const r1 = parseInt(color1.slice(1, 3), 16);
- const g1 = parseInt(color1.slice(3, 5), 16);
- const b1 = parseInt(color1.slice(5, 7), 16);
-
- const r2 = parseInt(color2.slice(1, 3), 16);
- const g2 = parseInt(color2.slice(3, 5), 16);
- const b2 = parseInt(color2.slice(5, 7), 16);
-
- // 线性插值
- const r = Math.round(r1 + (r2 - r1) * t);
- const g = Math.round(g1 + (g2 - g1) * t);
- const b = Math.round(b1 + (b2 - b1) * t);
-
- // 转回十六进制
- return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
- }
-
- // 根据进度计算角度
- function calculateAngle(progress) {
- // 将百分比转换为角度 (-144° 到 144°)
- return startAngleDeg + (progress / 100) * totalAngleDeg;
- }
-
- // 更新UI显示
- function updateUIDisplay(progress, color) {
- // 计算当前角度
- const angle = calculateAngle(progress);
-
- // 更新角度显示
- currentAngle.textContent = `${Math.round(angle)}°`;
-
- if (progress === 0 || color === null) {
- // 0%进度时,显示"无进度"
- currentColorText.textContent = '无进度';
- colorIndicator.style.backgroundColor = 'transparent';
- colorIndicator.style.boxShadow = 'none';
- percentText.style.color = 'rgba(255, 255, 255, 0.7)';
- percentText.style.textShadow = 'none';
- } else {
- // 大于0%进度时,显示对应颜色
- currentColorText.textContent = `当前颜色: ${color}`;
- colorIndicator.style.backgroundColor = color;
- colorIndicator.style.boxShadow = `0 0 15px ${color}`;
- percentText.style.color = color;
- percentText.style.textShadow = `0 0 20px ${color}`;
- }
-
- // 更新进度显示
- percentText.textContent = `${Math.round(progress)}%`;
- currentProgressSpan.textContent = Math.round(progress);
- progressSlider.value = Math.round(progress);
- }
-
- // 绘制背景圆环(包含空白区域指示)
- function drawBackgroundRing() {
- // 绘制完整背景圆
- ctx.beginPath();
- ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
- ctx.lineWidth = thickness;
- ctx.lineCap = 'round';
- ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
- ctx.stroke();
-
- // 绘制空白区域指示线(144°到-144°,经过上方)
- const blankStartRad = endAngleDeg * Math.PI / 180;
- const blankEndRad = (startAngleDeg + 360) * Math.PI / 180;
-
- ctx.beginPath();
- ctx.arc(centerX, centerY, radius - 5, blankStartRad, blankEndRad);
- ctx.lineWidth = 2;
- ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
- ctx.setLineDash([3, 3]);
- ctx.stroke();
- ctx.setLineDash([]);
- }
-
- // 绘制进度文字
- function drawProgressText(progress, color) {
- // 绘制百分比
- ctx.font = 'bold 42px Arial';
- ctx.textAlign = 'center';
- ctx.textBaseline = 'middle';
-
- if (progress === 0) {
- ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
- ctx.shadowColor = 'transparent';
- } else {
- ctx.fillStyle = color;
- ctx.shadowColor = color;
- ctx.shadowBlur = 15;
- }
-
- ctx.fillText(`${Math.round(progress)}%`, centerX, centerY);
-
- // 重置阴影
- ctx.shadowColor = 'transparent';
- ctx.shadowBlur = 0;
-
- // 添加当前角度标签
- ctx.font = '16px Arial';
- ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
-
- const angle = calculateAngle(progress);
- if (progress === 0) {
- ctx.fillText('起始角度: -144°', centerX, centerY + 40);
- } else {
- ctx.fillText(`当前角度: ${Math.round(angle)}°`, centerX, centerY + 40);
- }
- }
-
- // 绘制进度条(-144°到144°对称版本)
- function drawProgress(progress) {
- // 清空画布
- ctx.clearRect(0, 0, canvas.width, canvas.height);
-
- // 绘制背景圆环
- drawBackgroundRing();
-
- // 获取当前颜色(用于显示)
- const currentColor = progress === 0 ? null : getColorForPercent(progress);
-
- // 只有当进度大于0时才绘制进度条
- if (progress > 0) {
- // 计算结束角度(-144°到144°范围内)
- const endAngle = startAngleRad + (totalAngleRad * progress / 100);
-
- // 将圆弧分成足够多的段,以实现平滑渐变效果
- const segmentCount = Math.max(20, Math.floor(progress * 0.5));
- const angleRange = endAngle - startAngleRad;
-
- // 分段绘制圆弧
- for (let i = 0; i < segmentCount; i++) {
- const segmentStart = startAngleRad + (angleRange * i / segmentCount);
- const segmentEnd = startAngleRad + (angleRange * (i + 1) / segmentCount);
-
- // 计算这段中心点的百分比
- const segmentCenterPercent = (i + 0.5) / segmentCount * progress;
- const segmentColor = getColorForPercent(segmentCenterPercent);
-
- ctx.beginPath();
- ctx.arc(centerX, centerY, radius, segmentStart, segmentEnd);
- ctx.lineWidth = thickness;
- ctx.lineCap = 'round';
- ctx.strokeStyle = segmentColor;
- ctx.stroke();
- }
-
- // 在144度位置标记终点
- if (progress === 100) {
- const markerX = centerX + Math.cos(endAngle) * radius;
- const markerY = centerY + Math.sin(endAngle) * radius;
-
- ctx.beginPath();
- ctx.arc(markerX, markerY, 5, 0, Math.PI * 2);
- ctx.fillStyle = '#FE4F51';
- ctx.fill();
- ctx.strokeStyle = 'white';
- ctx.lineWidth = 2;
- ctx.stroke();
-
- // 也在-144度位置标记起点
- const startMarkerX = centerX + Math.cos(startAngleRad) * radius;
- const startMarkerY = centerY + Math.sin(startAngleRad) * radius;
-
- ctx.beginPath();
- ctx.arc(startMarkerX, startMarkerY, 5, 0, Math.PI * 2);
- ctx.fillStyle = '#FFC900';
- ctx.fill();
- ctx.strokeStyle = 'white';
- ctx.lineWidth = 2;
- ctx.stroke();
- }
- }
-
- // 绘制进度文字
- drawProgressText(progress, currentColor);
-
- // 更新UI显示
- updateUIDisplay(progress, currentColor);
- }
-
- // 动画函数
- function animateProgress() {
- if (animationId) {
- cancelAnimationFrame(animationId);
- }
-
- function animate() {
- // 使用缓动函数使动画更平滑
- const easing = 0.1;
- currentProgress += (targetProgress - currentProgress) * easing;
-
- // 如果接近目标值,直接设为目标值
- if (Math.abs(targetProgress - currentProgress) < 0.1) {
- currentProgress = targetProgress;
- } else {
- animationId = requestAnimationFrame(animate);
- }
-
- // 更新显示
- drawProgress(currentProgress);
- }
-
- animationId = requestAnimationFrame(animate);
- }
-
- // 更新进度
- function updateProgress(newProgress) {
- targetProgress = Math.max(0, Math.min(100, newProgress));
- animateProgress();
- }
-
- // 事件监听器
- progressSlider.addEventListener('input', function() {
- updateProgress(parseInt(this.value));
- });
-
- // 测试按钮事件
- testButtons.forEach(button => {
- button.addEventListener('click', function() {
- const value = parseInt(this.getAttribute('data-value'));
- updateProgress(value);
- });
- });
-
- // 动画演示按钮
- animateBtn.addEventListener('click', function() {
- // 从0到100的动画演示
- let progress = 0;
-
- function demoAnimation() {
- progress += 0.5; // 调整速度
-
- if (progress >= 100) {
- progress = 100;
- }
-
- updateProgress(progress);
-
- // 继续动画
- if (progress < 100) {
- requestAnimationFrame(demoAnimation);
- }
- }
-
- demoAnimation();
- });
-
- // 重置按钮
- resetBtn.addEventListener('click', function() {
- updateProgress(0);
- });
-
- // 跳转到144°按钮
- toEndBtn.addEventListener('click', function() {
- updateProgress(100); // 100% 对应 144°
- });
-
- // 随机进度按钮
- randomBtn.addEventListener('click', function() {
- const randomProgress = Math.floor(Math.random() * 101);
- updateProgress(randomProgress);
- });
-
- // 初始化角度显示
- startAngleDisplay.textContent = `${startAngleDeg}°`;
- endAngleDisplay.textContent = `${endAngleDeg}°`;
-
- // 初始化绘制
- drawProgress(currentProgress);
-
- // 绘制指南
- drawFullCircleGuide();
- drawSymmetricCircleGuide();
-
- console.log("对称288度环形进度条说明:");
- console.log("1. 进度条范围:-144° 到 144°(总计288°,占360°的80%)");
- console.log("2. 起始位置:-144°(相对于12点钟方向,位于7点钟方向)");
- console.log("3. 结束位置:144°(相对于12点钟方向,位于5点钟方向)");
- console.log("4. 空白区域:144° 到 216°(右侧72°空白)");
- console.log("5. 整个圆环已向左旋转90度,使0°基准位于12点钟方向");
- console.log("6. 角度计算公式:角度 = -144° + 进度百分比 × 288° / 100");
-
- // 初始测试
- setTimeout(() => {
- updateProgress(50); // 50% 对应 0°
- }, 500);
- </script>
- </body>
- </html>
|