Svelte, D3.js 및 SVG로 극좌표 그래프 애니메이션 풀기

베어링 속도 그래프의 작동 방식을 대화식으로 설명하기 위해 최근에 첫 번째 Svelte 앱을 만들었습니다. https://harryli0088.github.io/bearing-rate-graph/

설명의 일부로 저는 Smarter Every Day가 그의 비디오에서 수행한 것과 같은 언래핑 극좌표 그래프 애니메이션을 만들고 싶었습니다: https://youtu.be/AqqaYs7LjlM?t=447



이 애니메이션을 사용하여 만들었습니다.
  • Svelte의 모션tweened 기능, 튜토리얼 기반: https://svelte.dev/tutorial/tweened
  • D3.js를 사용한 보간' scaleLinear : https://github.com/d3/d3-scale
  • SVG linepath

  • 구현 방법



    Smarter Every Day의 애니메이션을 자세히 살펴본 후 포장 풀기 효과를 얻으려면 몇 가지 단계가 필요하다는 것을 깨달았습니다.
  • 극좌표 중심에서 BRG 사각형의 하단까지 선 끝점을 보간합니다
  • .
  • 극좌표에서 극좌표까지 선의 각도를 보간합니다.
    수직 위아래로
  • 일부 삼각법을 사용하여 BRG 사각형의 상단까지 극좌표 원의 가장자리 위치를 계산합니다
  • .

    1 단계



    주요 각도에서 원과 약간의 눈금으로 극좌표 그래프를 하드코딩하여 시작할 수 있습니다.

    $: ticks = [ //hard code the tick positions
        {angle:180,     label: "180°",      x1: 0,                       y1: halfHeight,               x2: 0, y2: 0},
        {angle:225,     label: "",          x1: -halfWidth/Math.sqrt(2), y1: -halfHeight/Math.sqrt(2), x2: 0, y2: 0},
        {angle:270,     label: "270°",      x1: -halfWidth,              y1: 0,                        x2: 0, y2: 0},
        {angle:315,     label: "",          x1: -halfWidth/Math.sqrt(2), y1: halfHeight/Math.sqrt(2),  x2: 0, y2: 0},
        {angle:0,       label: "0° (360°)", x1: 0,                       y1: -halfHeight,              x2: 0, y2: 0},
        {angle:45,      label: "",          x1: halfWidth/Math.sqrt(2),  y1: -halfHeight/Math.sqrt(2), x2: 0, y2: 0},
        {angle:90,      label: "90°",       x1: halfWidth,               y1: 0,                        x2: 0, y2: 0},
        {angle:135,     label: "",          x1: halfWidth/Math.sqrt(2),  y1: halfHeight/Math.sqrt(2),  x2: 0, y2: 0},
        {angle:179.999, label: "",          x1: 0,                       y1: halfHeight,               x2: 0, y2: 0},
      ]
    



    전체 1단계 코드: https://github.com/harryli0088/svelte-polar-animation-tutorial/blob/main/src/Step1.svelte

    2 단계



    다음으로 DOM의 값을 자동으로 업데이트하는 Svelte의 모션 패키지에서 tweened를 도입할 수 있습니다. animation 값을 주기적으로 변경하도록 간격을 설정할 수도 있습니다.

    import { onDestroy } from 'svelte'
    import { tweened } from 'svelte/motion'
    import { cubicOut } from 'svelte/easing'
    
    const animation = tweened(0, {
      duration: 4000,
      easing: cubicOut
    })
    const interval = setInterval(() => {
      animation.set($animation===1 ? 0 : 1)
    }, 5000)
    onDestroy(() => clearInterval(interval))
    



    전체 2단계 코드: https://github.com/harryli0088/svelte-polar-animation-tutorial/blob/main/src/Step2.svelte

    3단계



    다음으로 우리는 극원의 중심에서 BRG 직사각형의 하단까지 line 끝점을 보간해야 합니다.

    x2



    x2의 값은 물론 우리가 보고 있는 각도에 따라 달라집니다. 애니메이션에서 모든 각도가 원의 중심에서 시작하는 것을 볼 수 있습니다. 애니메이션이 끝날 때까지 각도(180° -> 360° 또는 0° -> 180°)는 x 위치(왼쪽 -> 중앙 -> 오른쪽)에서 끝납니다. 다음과 같이 D3.js'scaleLinear를 사용하여 이 보간을 나타낼 수 있습니다.

    $: x2Scale = scaleLinear().domain(
      [0, 180, 180, 360]
    ).range(
      [
        0,
        $animation*halfWidth,
        -$animation*halfWidth,
        0,
      ]
    )
    

    y2



    y2 단순은 모든 라인에 대해 원의 중심에서 svg의 맨 아래로 이동합니다.

    $: y2 = $animation * halfHeight
    



    전체 3단계 코드: https://github.com/harryli0088/svelte-polar-animation-tutorial/blob/main/src/Step3.svelte

    4단계



    다음은 가장 까다로운 부분입니다. 우리는 선이 풀리고 위아래로 똑바로 위치하기를 원합니다. 이는 모든 선의 각도가 시작 각도에서 0°로 전환됨을 의미합니다. 예를 들어 각도가 270°인 선은 270°에서 시작하여 0°로 끝납니다. 135°의 선은 135°에서 시작하여 0°에서 끝납니다. 내 코드에서는 이 전이 각도theta라고 합니다. 각도 180° 이상은 360°로 전환되고 각도 180° 이하에서는 0°로 전환되도록 해야 합니다. (예를 들어 270°가 -90°가 되도록 >=180° 각도를 360°로 빼서 첫 번째 부분을 수행합니다.)

    $: thetaScale = scaleLinear().domain(
      [0, 180, 180, 360]
    ).range(
      [0, (1 - $animation)*180, ($animation - 1)*180, 0]
    )
    

    그런 다음 theta를 취하고 삼각법을 사용하여 line 끝점 x1y1를 계산할 수 있습니다.

    $: getLineDataFromAngle = (angle, radius=halfWidth) => {
      const x2 = x2Scale(angle)
      const theta = thetaScale(angle) / DEG_PER_RAD
      return {
        x1: x2 + radius * Math.sin(theta),
        y1: - radius * Math.cos(theta),
        x2, y2,
      }
    }
    



      $: lineData = ticks.map(t => getLineDataFromAngle(t.angle))
    

    또한 circle 를 사용하는 대신 필요한 방식으로 애니메이션을 적용할 수 없습니다. 다음과 같이 SVGpath 를 사용하여 원에서 선으로의 전환을 가짜로 만들 수 있습니다.

    const circleDegrees:number[] = []
    for(let i=180; i<360; ++i) {
      circleDegrees.push(i)
    }
    for(let i=0; i<180; ++i) {
      circleDegrees.push(i)
    }
    circleDegrees.push(179.9) //this is now equal to [180, 181, ..., 359, 0, 1, ..., 179, 179.9]
    $: topFactor = (12 + 3*$animation) / 16
    $: circlePathRadius = halfWidth * topFactor
    $: circlePath = circleDegrees.reduce(
      (d, degree) => {
        const { x1, y1 } = getLineDataFromAngle(degree, circlePathRadius)
        d += ` ${x1},${y1}`
        return d
      },
      "M"
    )
    



    전체 4단계 코드: https://github.com/harryli0088/svelte-polar-animation-tutorial/blob/main/src/Step4.svelte

    최종 결과



    마지막으로 다음과 같이 애니메이션에 조정을 추가할 수 있습니다.
  • 시간 축에 패딩 추가
  • 시간축의 트랜지션
  • 각도 레이블에 대한 Dy 변경



  • 소스 코드



    레포:


    harryli0088 / svelte-polar-animation-튜토리얼





    최종 Svelte 구성 요소 코드:





    좋은 웹페이지 즐겨찾기