오픈 소스 모험: 에피소드 34: Last Russian Tank Predictor를 모바일 친화적으로 만들기

18701 단어 javascriptd3svelte
지금까지 가지고 있는 앱에는 두 가지 큰 문제가 있습니다.
  • Array.prototype.at가 없기 때문에 Safari에서 작동하지 않습니다.
  • 작은 모바일 화면에서 그래프가 화면을 약간 넘칩니다
  • .

    웹 앱을 개발하는 가장 일반적인 방법은 데스크톱 Chrome에서 먼저 작동하도록 만드는 것입니다(데스크톱 Firefox도 작동함). 이미 전화가 아닌 컴퓨터에서 코딩하고 있고 Chrome에는 가장 완전한 기능 세트가 있으므로 최고의 개발자 도구.

    요즘은 크롬과 크롬과 유사한 브라우저가 매우 우세하고 파이어폭스가 ​​그들과 대부분 호환되어 꽤 잘 작동하며 대부분의 웹사이트는 어디에서나 작동할 것입니다.

    마지막으로 현재 Graph 구성 요소를 계산을 수행하는 TankLosses 부모와 표시만 수행하는 Graph로 분할합니다.

    원정 여행



    Safari는 항상 몇 년 뒤쳐져 있습니다. 복잡한 polyfill 솔루션이 있지만 이 경우 수정해야 하는 코드 한 줄에 불과합니다. 대신:

    adjustedData.at(-1)
    


    우리는 할 수있어:

    adjustedData[adjustedData.length - 1]
    


    확실히 못생겼지만 전체 폴리필 시스템을 설정할 가치가 없는 아주 작은 것 중 하나일 뿐입니다.

    작은 화면 지원



    대상의 크기를 알고 있으면 어떤 크기도 지원하는 것보다 그래프를 작성하는 것이 훨씬 쉽습니다. 데스크톱에서는 작동하지만 모바일에서는 잘 작동하지 않습니다.

    다행스럽게도 SVG에는 멋진 트릭이 있으며 별도의 외부 크기(장치에 따라 다름)와 내부 크기(일정하며 모든 것을 올바른 위치에 배치하는 데 사용함)를 지원합니다.
    <svg viewBox="0 0 800 600"> 로 내부 크기를 선언한 다음 다음으로 외부 크기를 선언할 수 있습니다.

    svg {
      width: 800px;
      max-width: 100vw;
      display: block;
    }
    


    src/TankLosses.svelte



    다음은 데이터의 비동기 로드를 제외한 전체 앱을 담당하는 상위TankLosses 구성 요소입니다. 일반적으로 외부에 보관하는 것이 가장 좋습니다.

    <script>
    import * as d3 from "d3"
    import Form from "./Form.svelte"
    import Graph from "./Graph.svelte"
    
    export let data
    
    let adjust = (data, adjustmentLoss) => data.map(({date, tank}) => ({date, tank: Math.round(tank * (1 + adjustmentLoss/100))}))
    
    // put some dummy data to avoid issues with initialization order
    let adjustmentLoss = 0, futureIntensity = 100, totalTanks = 0
    
    let [minDate, maxDate] = d3.extent(data, d => d.date)
    
    $: adjustedData = adjust(data, adjustmentLoss)
    $: alreadyDestroyedTanks = d3.max(adjustedData, d => d.tank)
    $: tanksMax = Math.max(alreadyDestroyedTanks, totalTanks)
    
    $: currentTankRate = alreadyDestroyedTanks / (maxDate - minDate)
    $: futureTankRate = (currentTankRate * futureIntensity / 100.0)
    $: tanksTodo = totalTanks - alreadyDestroyedTanks
    $: lastTankDate = new Date(+maxDate + (tanksTodo / futureTankRate))
    
    $: xScale = d3.scaleTime()
      .domain([minDate, lastTankDate])
      .range([0, 700])
    
    $: yScale = d3.scaleLinear()
      .domain([0, tanksMax])
      .nice()
      .range([500, 0])
    
    $: pathData = d3.line()
      .x(d => xScale(d.date))
      .y(d => yScale(d.tank))
      (adjustedData)
    
    $: trendPathData = d3.line()
      .x(d => xScale(d.date))
      .y(d => yScale(d.tank))
      ([adjustedData[0], adjustedData[adjustedData.length - 1], {tank: totalTanks, date: lastTankDate}])
    
    $: tankTotalPathData = d3.line()
      .x(xScale)
      .y(yScale(tanksMax))
      ([minDate, lastTankDate])
    
    $: xAxis = d3.axisBottom()
      .scale(xScale)
      .tickFormat(d3.timeFormat("%e %b %Y"))
    
    $: yAxis = d3
      .axisLeft()
      .scale(yScale)
    </script>
    
    <h1>Russian Tank Losses</h1>
    <Graph {pathData} {trendPathData} {tankTotalPathData} {xAxis} {yAxis}/>
    <Form bind:adjustmentLoss bind:futureIntensity bind:totalTanks />
    <div>Russia will lose its last tank on {d3.timeFormat("%e %b %Y")(lastTankDate)}</div>
    


    src/Graph.svelte



    다음은 Graph 구성 요소로, 이미 계산된 데이터를 표시하는 역할만 합니다.

    <script>
    import Axis from "./Axis.svelte"
    export let pathData, trendPathData, tankTotalPathData, xAxis, yAxis
    </script>
    
    <svg viewBox="0 0 800 600">
      <g class="graph">
        <path class="data" d={pathData}/>
        <path class="trendline" d={trendPathData}/>
        <path class="tanktotal" d={tankTotalPathData}/>
      </g>
      <g class="x-axis"><Axis axis={xAxis}/></g>
      <g class="y-axis"><Axis axis={yAxis}/></g>
    </svg>
    
    <style>
    svg {
      width: 800px;
      max-width: 100vw;
      display: block;
    }
    .graph {
      transform: translate(50px, 20px);
    }
    path {
      fill: none;
    }
    path.data {
      stroke: red;
      stroke-width: 1.5;
    }
    path.trendline {
      stroke: red;
      stroke-width: 1.5;
      stroke-dasharray: 3px;
    }
    path.tanktotal {
      stroke: blue;
      stroke-width: 1.5;
    }
    .x-axis {
      transform: translate(50px, 520px);
    }
    .y-axis {
      transform: translate(50px, 20px);
    }
    </style>
    


    지금까지의 이야기



    All the code is on GitHub .

    저는 이것을 GitHub Pagesyou can see it here에 배포했습니다.

    다음에 온다



    다음화에서는 앱으로 몇 가지 더 해보도록 하겠습니다.

    좋은 웹페이지 즐겨찾기