散點圖 scatter chart

基本散點圖
          
            // html 
            <div class="basicScatter"></div>

            // JS-建立 svg
            const  basicScatter = async()=>{
              const svgWidth = parseInt(d3.select('.basicScatter').style('width')),
                    svgHeight = 500
                    margin = 50
              const svg = d3.select('.basicScatter')
                            .append('svg')
                            .attr('width', svgWidth)
                            .attr('height', svgHeight)
      
              // 取資料
              const cors = "https://cors-anywhere.herokuapp.com/";
              const url = "https://raw.githubusercontent.com/holtzy/data_to_viz/
                          master/Example_dataset/2_TwoNum.csv"
              const data = await d3.csv(`${cors}${url}`);
      
              // 建立X軸線
              const xScale = d3.scaleLinear()
                    .domain([0,4000])
                    .range([0, (svgWidth - margin*2)])
      
              const xAxisGenerator = d3.axisBottom(xScale)               
      
              svg.append('g')
                .attr('transform', `translate(${margin}, ${svgHeight - margin/2})`)
                .call(xAxisGenerator)
      
              // 建立Y軸線
              console.log(data)
              const yScale = d3
                    .scaleLinear()
                    .domain([0,500000])
                    .range([(svgHeight - margin), 0])
      
              const yAxisGenerator = d3.axisLeft(yScale).tickFormat(d=>'$'+d) 
      
              svg.append('g')
                .attr('transform', `translate(${margin}, ${margin/2})`)
                .call(yAxisGenerator)
      
              // 加上點點
              svg.append('g')
                .selectAll('dot')
                .data(data)
                .join('circle')
                .attr('cx', d => xScale(d.GrLivArea))
                .attr('cy', d => yScale(d.SalePrice))
                .attr('r', 1.5)
                .style('fill', d => d.SalePrice > 129000? 'pink':'#69b3a2')
            }
          
        
互動散點圖-hover
          
            // html 
            <div class="hoverScatter"></div>
            
            // js-建立svg
            const width = parseInt(d3.select(".hoverScatter").style("width")),
                height = 500,
                margin = {
                      top: 40,
                      right: 20,
                      bottom: 20,
                      left: 40
                    },
                radius = 5;
    
            const svg = d3.select(".hoverScatter")
                          .append("svg")
                          .attr('width', width)
                          .attr('height', height);
    
            const dataset = [
              { x: 100,y: 110 },{ x: 83,y: 43 },{ x: 92,y: 28 },
              { x: 49,y: 74 },{ x: 51,y: 10 },{ x: 25,y: 98 },
              { x: 77,y: 30},{ x: 20,y: 83 },{ x: 11,y: 63 },
              { x: 4,y: 55 },{ x: 0,y: 0 },{ x: 85,y: 100 },
              { x: 60,y: 40 },{ x: 70,y: 80 },{ x: 10,y: 20 },
              { x: 40,y: 50 },{ x: 25,y: 31 }
            ];
    
            // 建立X比例尺與軸線
            const xScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, (d) => d.x + 10)])
                .range([margin.left, width - margin.right]);
    
            const xAxisGenerator = d3
                    .axisBottom()
                    .scale(xScale)
    
            const xAxis = svg.append('g')
                .attr('class', 'xAxis')
                .attr('transform', `translate(0, ${height - margin.bottom})`)
                .call(xAxisGenerator)
            
            // 建立Y比例尺與軸線
            const yScale = d3
                  .scaleLinear()
                  .domain([d3.max(dataset, (d)=> d.y + 10), 0])
                  .range([margin.top, height - margin.bottom])
    
            const yAxisGenerator = d3
                      .axisLeft()
                      .scale(yScale)
    
            const yAxis = svg
                  .append('g')
                  .attr('class', 'yAxis')
                  .attr('transform', `translate(${margin.left}, 0)`)
                  .call(yAxisGenerator)
    
            // 綁定資料並建立圓點
            svg.selectAll("circle")
                .data(dataset)
                .join("circle")
                .attr('cx', d => xScale(d.x))
                .attr('cy', d => yScale(d.y))
                .attr('r', 5)
                .attr('fill', '#000')
                .on("mouseover", handleMouseOver)
                .on("mouseout", handleMouseOut);
    
            //  mouseover 時點點變色+tooltip
            function handleMouseOver(d, i) {
                // 選定this的元素,改變hover過去的顏色跟形狀
                d3.select(this)
                    .attr('fill', 'orange')
                    .attr('r', radius * 2)
                    .style('cursor', 'pointer')
    
                // 加上tooltips
                let pt = d3.pointer(event)
                svg.append("text")
                    .attr('class', 'hoverTextInfo')
                    .attr('x', pt[0] + 10)
                    .attr('y', pt[1] - 10)
                    .style('fill', 'red')
                    .text([`x:${event.target.__data__.x}, 
                         y:${event.target.__data__.y}`])
            }
    
            // mouseleave 時變回原樣
            function handleMouseOut(d, i) {
                d3.selectAll('.hoverTextInfo').remove()
                d3.select(this)
                    .attr('fill', 'black')
                    .attr('r', radius)
            }
          
        
互動散點圖-畫布上新增點點
          
            // html 
            <div class="addPointScatter"></div>

            // 建立svg
            const width = parseInt(d3.select(".addPointScatter").style("width")),
                height = 500,
                margin = {
                      top: 40,
                      right: 20,
                      bottom: 20,
                      left: 40
                    },
                radius = 5;
    
            const svg = d3.select(".addPointScatter")
                          .append("svg")
                          .attr('width', width)
                          .attr('height', height)
                          .style('cursor', 'pointer');
    
            const dataset = [
              { x: 100,y: 110 },{ x: 83,y: 43 },{ x: 92,y: 28 },
              { x: 49,y: 74 },{ x: 51,y: 10 },{ x: 25,y: 98 },
              { x: 77,y: 30},{ x: 20,y: 83 },{ x: 11,y: 63 },
              { x: 4,y: 55 },{ x: 0,y: 0 },{ x: 85,y: 100 },
              { x: 60,y: 40 },{ x: 70,y: 80 },{ x: 10,y: 20 },
              { x: 40,y: 50 },{ x: 25,y: 31 }
            ];
    
            // 建立X比例尺與軸線
            const xScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, (d) => d.x + 10)])
                .range([margin.left, width - margin.right]);
    
            const xAxisGenerator = d3
                    .axisBottom()
                    .scale(xScale)
    
            const xAxis = svg.append('g')
                .attr('class', 'xAxis')
                .attr('transform', `translate(0, ${height - margin.bottom})`)
                .call(xAxisGenerator)
            
            // 建立Y比例尺與軸線
            const yScale = d3
                  .scaleLinear()
                  .domain([d3.max(dataset, (d)=> d.y + 10), 0])
                  .range([margin.top, height - margin.bottom])
    
            const yAxisGenerator = d3
                      .axisLeft()
                      .scale(yScale)
    
            const yAxis = svg
                  .append('g')
                  .attr('class', 'yAxis')
                  .attr('transform', `translate(${margin.left}, 0)`)
                  .call(yAxisGenerator)
    
            // 綁定資料並建立圓點
            svg.selectAll("circle")
                .data(dataset)
                .join("circle")
                .attr('cx', d => xScale(d.x))
                .attr('cy', d => yScale(d.y))
                .attr('r', 5)
                .attr('fill', '#000')
    
            // 滑鼠click的時候增加一個點
            svg.on("click", (e) => {
              const coords = d3.pointer(e);
    
              // 把XY座標軸轉換成資料
              const newData = {
                  x: Math.round(xScale.invert(coords[0])),
                  y: Math.round(yScale.invert(coords[1]))
              };
    
              // 將增加的資料座標推入原本的data
              dataset.push(newData);
    
              // 將新的資料綁定上circle
              svg.selectAll("circle")
                  .data(dataset)
                  .join("circle")
                  .attr('cx', d => xScale(d.x))
                  .attr('cy', d => yScale(d.y))
                  .attr('r', 5)
                  .attr('fill', '#000')
              })