Code Weekly Downloads Chart trên npmjs.com

Mở đầu Là một lập trình viên Web chắc hẳn ai cũng biết đến và từng truy cập website npmjs.com để khám phá, tìm hiểu (lấy link install ) các thư viện Javascript. Và mỗi lần truy cập mình điều thấy khá thích thú với biểu đồ Weekly Downloads, vậy nên mình sẽ cùng với

Mở đầu 👋

Là một lập trình viên Web chắc hẳn ai cũng biết đến và từng truy cập website npmjs.com để khám phá, tìm hiểu (lấy link install 😂) các thư viện Javascript. Và mỗi lần truy cập mình điều thấy khá thích thú với biểu đồ Weekly Downloads, vậy nên mình sẽ cùng với các bạn tìm hiểu cách tạo ra một component tương tự nhé 😎

Công nghệ mình dùng cho project là ReactJS và SVG


Nội dung

Data

data = [{
    downloads:111154,
    label: '2020-07-12 to 2020-07-18',},{
    downloads:111834,
    label: '2020-07-19 to 2020-07-25',},
  ...
]

Mình sử dụng định dạng data như trên, đấy cũng là dạng dữ liệu mà mà component của npmjs.com sử dụng (nhưng mình tìm toét cả mắt ở tag Network thì không thấy responce nào trả về như thế 😕 ai tìm được chỉ mình với nha)

Phần biểu đồ tim tím

constWIDTH=500constHEIGHT=100// ...<svg width={WIDTH} height={HEIGHT}><path d={pathD1} fill='none'></path><path d={pathD2} stroke='none'></path><line x1={pointSelected.x} x2={pointSelected.x} y1={0} y2={HEIGHT}></line><circle cx={pointSelected.x} cy={pointSelected.y}></circle><rect
       width={WIDTH}
       height={HEIGHT}
       onMouseMove={handleMouseMove}
       onMouseOut={handleMouseOut}></rect></svg>// ...

Bên trong thẻ svg mình dùng một số thẻ nữa với chức năng như sau

  • thẻ path đầu tiên để vẽ đường xanh tím này
  • thẻ path thứ high để vẽ hình tím tím này
  • thẻ linecircle để vẽ hình này khi ta hover trên biểu đồ
  • còn thẻ rect để ta bắt sự kiện onMouseMoveonMouseOut khi hover chuột để xem chi tiết lượt download của từng tuần

Path & Path

Phần quan trọng nhất là làm sao chúng ta có thể tính được pathD để vẽ được biểu đồ trên bằng dữ liệu bên trên. Mỗi tuần mình sẽ cho là 1 điểm có tọa độ x , y , số lượt downloads, và label là thời gian tuần đó {x, y, downloads, label} .Đầu tiên chúng ta sẽ tính toán tọa độ các điểm:

constWIDTH=500constHEIGHT=100const{ min, max }=findMinMax(data)// tìm giá trị download min và max (findMinMax là mình tự viết)const deltaX =WIDTH/(data.length -1)// tính khoảng cách theo trục X giữa 2 điểm const deltaY =60/(max - min)// ở đây chiều cao chart của mình là 100px mình muốn tuần có //lượt download lớn nhất ở tọa độ Y 0 tuần có lượt download thấp nhất ở tọa độ Y 60 //nên mình tính deltaY theo từng lượt download như trên//đây là phầm tính tọa độ các điểmlet points =[]for(const[index, value]of data.entries()){
  points.push({
    x: index * deltaX,
    y:(max - value.downloads)* deltaY +8,// tất cả +8 để cho đẹp chút thôi
    downloads: value.downloads,
    label: value.label,})}

Tiếp theo ta sẽ tính biến pathD. ở đây mình dùng attribute d của thẻ path, nó sẽ bao gồm một dãy các kí tự trong đó có một chữ cái đi kèm tọa độ x y. Ở đây mình dùng những kí tự đơn giản sau (khó mình không biết 😒)

  • M 20 40 : luôn bắt đầu với M để di chuyển bút vẽ đến tọa {20,40}
  • L 40 60 : vẽ 1 đường thẳng từ điểm trước đó của bút đến tọa độ {40,60}
  • Z : đóng lại đường đã vẽ thành một hình (màu trong hình được định nghĩa bằng attribute fill)
let pathD =`M ${points[0].x}${points[0].y}`for(const point of points){
  pathD +=`L ${point.x}${point.y}`}const pathD1 = pathD
const pathD2 =`${pathD} L ${WIDTH}${HEIGHT} L 0 ${HEIGHT} Z`

Vậy là phần khó đã xong (style mình sẽ để riêng 1 file scss sau)

Bắt sự kiện Mouse và show thông tin chi tiết

//...const defaultPoint ={// giá trị mặc định của điểm là không hiển thị trên svg
 x:-1000,
 y:-1000,
 downloads: data[data.length -1].downloads,
 label:'Weekly Downloads',}functionChart(){const[pointSelected, setPointSelected]=useState(defaultPoint)consthandleMouseMove=(e)=>{let index = Math.round(e.nativeEvent.offsetX / deltaX)// xác định tọa độ theo trục X chuột đang di chuyển trên để tìm pointsetPointSelected(points[index])}consthandleMouseOut=(e)=>{setPointSelected(defaultPoint)}return(<div className='chart__container'><h1>{pointSelected.label}</h1><div className='content'><div className='num'>{Intl.NumberFormat().format(pointSelected.downloads)}</div>// format lượt download dạng ###,###,###<div className='chart'><svg width={WIDTH} height={HEIGHT}><path d={pathD1} fill='none'></path><path d={pathD2} stroke='none'></path><line x1={pointSelected.x} x2={pointSelected.x} y1={0} y2={HEIGHT}></line><circle cx={pointSelected.x} cy={pointSelected.y}></circle>// các thuộc tính x1,x2 của line, cx, cy, r của circle các bạn tự tìm hiểu thêm nha<rect
             width={WIDTH}
             height={HEIGHT}
             onMouseMove={handleMouseMove}
             onMouseOut={handleMouseOut}></rect></svg></div></div></div>)}

Vậy là được rồi đấy các bạn 👏

Đây là phần
demo


Kết luận

Đây là bài viểt debut của mình, có gì sai sót mong các bạn, các anh chị góp ý

Hi vọng với chút chia sẻ này sẽ giúp đỡ được mọi người, đặc biệt là các bạn mới để cộng đồng DEV Việt Nam ngày càng phát triển! 👏

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ