|
420 | 420 | H = canvas.height = canvas.offsetHeight; |
421 | 421 | } |
422 | 422 |
|
423 | | - const towerPositions = [0.1, 0.3, 0.5, 0.7, 0.9]; |
424 | | - const towerHeights = [50, 65, 80, 65, 50]; |
425 | | - const sparkPos = { t: 0, i: 0 }; |
426 | | - let sparkDir = 1; |
| 423 | + // Predefined tower points for the background grid |
| 424 | + const nodes = [ |
| 425 | + { x: 0.1, y: 0.3, label: '2026' }, |
| 426 | + { x: 0.25, y: 0.6, label: '2026' }, |
| 427 | + { x: 0.45, y: 0.25, label: '2021' }, |
| 428 | + { x: 0.65, y: 0.7, label: '2015' }, |
| 429 | + { x: 0.85, y: 0.4, label: '2012' } |
| 430 | + ]; |
427 | 431 |
|
428 | | - function drawMini() { |
429 | | - ctx.clearRect(0, 0, W, H); |
| 432 | + function drawTower(node) { |
| 433 | + const tx = node.x * W; |
| 434 | + const ty = node.y * H; |
| 435 | + const size = 30; |
430 | 436 |
|
431 | | - // Ground line |
432 | 437 | ctx.save(); |
433 | | - ctx.strokeStyle = 'rgba(59,139,255,0.15)'; |
434 | | - ctx.lineWidth = 1; |
| 438 | + ctx.strokeStyle = 'rgba(60, 139, 255, 0.4)'; |
| 439 | + ctx.lineWidth = 1.5; |
| 440 | + |
| 441 | + // Tower body |
435 | 442 | ctx.beginPath(); |
436 | | - ctx.moveTo(0, H - 2); |
437 | | - ctx.lineTo(W, H - 2); |
| 443 | + ctx.moveTo(tx, ty); |
| 444 | + ctx.lineTo(tx - 10, ty + size); |
| 445 | + ctx.lineTo(tx + 10, ty + size); |
| 446 | + ctx.closePath(); |
438 | 447 | ctx.stroke(); |
439 | | - ctx.restore(); |
440 | 448 |
|
441 | | - // Towers |
442 | | - const tPos = towerPositions.map(p => ({ x: p * W, h: towerHeights[towerPositions.indexOf(p)] })); |
| 449 | + // Crossarm |
| 450 | + ctx.beginPath(); |
| 451 | + ctx.moveTo(tx - 8, ty + size * 0.4); |
| 452 | + ctx.lineTo(tx + 8, ty + size * 0.4); |
| 453 | + ctx.stroke(); |
443 | 454 |
|
444 | | - tPos.forEach(tp => { |
445 | | - ctx.save(); |
446 | | - ctx.strokeStyle = 'rgba(96,160,224,0.6)'; |
447 | | - ctx.lineWidth = 1.5; |
448 | | - // main mast |
449 | | - ctx.beginPath(); |
450 | | - ctx.moveTo(tp.x, H - 2); |
451 | | - ctx.lineTo(tp.x, H - tp.h - 2); |
452 | | - ctx.stroke(); |
453 | | - // crossarm |
454 | | - const armW = tp.h * 0.5; |
455 | | - ctx.beginPath(); |
456 | | - ctx.moveTo(tp.x - armW / 2, H - tp.h * 0.55 - 2); |
457 | | - ctx.lineTo(tp.x + armW / 2, H - tp.h * 0.55 - 2); |
458 | | - ctx.stroke(); |
459 | | - // insulator dots |
460 | | - ctx.fillStyle = 'rgba(96,160,224,0.8)'; |
461 | | - ctx.beginPath(); |
462 | | - ctx.arc(tp.x - armW / 2, H - tp.h * 0.55 - 2, 2, 0, Math.PI * 2); |
463 | | - ctx.fill(); |
464 | | - ctx.beginPath(); |
465 | | - ctx.arc(tp.x + armW / 2, H - tp.h * 0.55 - 2, 2, 0, Math.PI * 2); |
466 | | - ctx.fill(); |
467 | | - ctx.restore(); |
468 | | - }); |
| 455 | + // Insulator dots |
| 456 | + ctx.fillStyle = '#3b8bff'; |
| 457 | + ctx.beginPath(); |
| 458 | + ctx.arc(tx, ty, 2, 0, Math.PI * 2); |
| 459 | + ctx.fill(); |
| 460 | + ctx.restore(); |
| 461 | + } |
469 | 462 |
|
470 | | - // Wires |
471 | | - for (let i = 0; i < tPos.length - 1; i++) { |
472 | | - const p1 = { x: tPos[i].x, y: H - tPos[i].h * 0.55 - 2 - 2 }; |
473 | | - const p2 = { x: tPos[i + 1].x, y: H - tPos[i + 1].h * 0.55 - 2 - 2 }; |
474 | | - const mx = (p1.x + p2.x) / 2; |
475 | | - const my = Math.max(p1.y, p2.y) + 8; |
476 | | - ctx.save(); |
477 | | - ctx.strokeStyle = 'rgba(42,80,128,0.8)'; |
478 | | - ctx.lineWidth = 1; |
479 | | - ctx.beginPath(); |
480 | | - ctx.moveTo(p1.x, p1.y); |
481 | | - ctx.quadraticCurveTo(mx, my, p2.x, p2.y); |
482 | | - ctx.stroke(); |
483 | | - ctx.restore(); |
484 | | - } |
| 463 | + function drawBendingLine(n1, n2) { |
| 464 | + const x1 = n1.x * W; |
| 465 | + const y1 = n1.y * H; |
| 466 | + const x2 = n2.x * W; |
| 467 | + const y2 = n2.y * H; |
| 468 | + |
| 469 | + ctx.save(); |
| 470 | + ctx.strokeStyle = 'rgba(60, 139, 255, 0.15)'; |
| 471 | + ctx.lineWidth = 1; |
| 472 | + ctx.setLineDash([5, 5]); |
485 | 473 |
|
486 | | - // Spark |
487 | | - const i = sparkPos.i; |
488 | | - if (i < tPos.length - 1) { |
489 | | - const t = sparkPos.t; |
490 | | - const p1 = { x: tPos[i].x, y: H - tPos[i].h * 0.55 - 2 - 2 }; |
491 | | - const p2 = { x: tPos[i + 1].x, y: H - tPos[i + 1].h * 0.55 - 2 - 2 }; |
492 | | - const mx = (p1.x + p2.x) / 2; |
493 | | - const my = Math.max(p1.y, p2.y) + 8; |
| 474 | + ctx.beginPath(); |
| 475 | + ctx.moveTo(x1, y1); |
| 476 | + |
| 477 | + // Bending logic: move horizontally then vertically |
| 478 | + const midX = x1 + (x2 - x1) * 0.5; |
| 479 | + ctx.lineTo(midX, y1); |
| 480 | + ctx.lineTo(midX, y2); |
| 481 | + ctx.lineTo(x2, y2); |
| 482 | + |
| 483 | + ctx.stroke(); |
| 484 | + ctx.restore(); |
| 485 | + } |
494 | 486 |
|
495 | | - const sx = (1 - t) * (1 - t) * p1.x + 2 * (1 - t) * t * mx + t * t * p2.x; |
496 | | - const sy = (1 - t) * (1 - t) * p1.y + 2 * (1 - t) * t * my + t * t * p2.y; |
| 487 | + function draw() { |
| 488 | + ctx.clearRect(0, 0, W, H); |
497 | 489 |
|
498 | | - ctx.save(); |
499 | | - ctx.fillStyle = '#f0c040'; |
500 | | - ctx.shadowBlur = 8; |
501 | | - ctx.shadowColor = '#f0c040'; |
502 | | - ctx.beginPath(); |
503 | | - ctx.arc(sx, sy, 2, 0, Math.PI * 2); |
504 | | - ctx.fill(); |
505 | | - ctx.restore(); |
| 490 | + // Draw connecting lines |
| 491 | + for (let i = 0; i < nodes.length - 1; i++) { |
| 492 | + drawBendingLine(nodes[i], nodes[i+1]); |
506 | 493 | } |
507 | | - } |
508 | 494 |
|
509 | | - function animate() { |
510 | | - sparkPos.t += 0.012 * sparkDir; |
511 | | - if (sparkPos.t >= 1) { |
512 | | - sparkPos.t = 1; |
513 | | - if (sparkPos.i < towerPositions.length - 2) { |
514 | | - sparkPos.i++; |
515 | | - sparkPos.t = 0; |
516 | | - } else { |
517 | | - sparkDir = -1; |
518 | | - sparkPos.t = 1; |
519 | | - } |
520 | | - } else if (sparkPos.t <= 0) { |
521 | | - sparkPos.t = 0; |
522 | | - if (sparkPos.i > 0) { |
523 | | - sparkPos.i--; |
524 | | - sparkPos.t = 1; |
525 | | - } else { |
526 | | - sparkDir = 1; |
527 | | - sparkPos.t = 0; |
528 | | - } |
529 | | - } |
530 | | - drawMini(); |
531 | | - requestAnimationFrame(animate); |
| 495 | + // Draw towers |
| 496 | + nodes.forEach(node => drawTower(node)); |
532 | 497 | } |
533 | 498 |
|
534 | | - window.addEventListener('load', () => { |
535 | | - resize(); |
536 | | - animate(); |
537 | | - }); |
538 | | - window.addEventListener('resize', () => { |
539 | | - resize(); |
540 | | - }); |
| 499 | + window.addEventListener('load', () => { resize(); draw(); }); |
| 500 | + window.addEventListener('resize', () => { resize(); draw(); }); |
541 | 501 | })(); |
542 | 502 |
|
543 | 503 |
|
|
0 commit comments