PerfectVector
Bởi Irene Kim10 phút đọc

Canvas Path2D SVG: Tái sử dụng đường dẫn đúng cỡ

Tái sử dụng dữ liệu đường dẫn SVG trên canvas với biến đổi viewBox, màu, lỗ và mật độ pixel đúng. Làm theo ví dụ tự tạo và giữ bản gốc SVG chỉnh sửa được.

Trên trang này

Truyền thuộc tính d của đường dẫn vào new Path2D(d), rồi vẽ bằng ngữ cảnh canvas. Để khớp SVG, cũng cần chuyển ánh xạ tọa độ, màu tô và quy tắc tô của nó. Hàm khởi tạo nhận dữ liệu đường dẫn; không nhập tài liệu SVG hay áp dụng viewBox, CSS hoặc phép biến đổi nhóm của tài liệu (hàm khởi tạo Path2D trên MDN).

Điều này quan trọng khi biểu tượng tùy chỉnh trông đúng ở SVG nhưng trở nên nhỏ, lệch, đen hoặc bị tô kín lỗ trên canvas. Ví dụ dưới sửa các khác biệt đó bằng hai đường dẫn và phép biến đổi làm vừa chỉ định rõ. Bắt đầu với hình học vector sẵn có khi bạn có. Vector hóa chỉ hữu ích khi phần hình cần tái sử dụng vẫn ở dạng pixel.

Đối tượng Path2D mang theo gì

Hãy xem dữ liệu đường dẫn là mô tả đường bao. Các chỉ dẫn SVG khác nằm ngoài chuỗi đó, và mã vẽ canvas phải cung cấp những gì nó cần.

Đầu vào SVGTrách nhiệm phía canvas
Giá trị d của đường dẫnTạo hình học Path2D
viewBox và kích thước hiển thịTính phép biến đổi vẽ
fill và fill-ruleĐặt fillStyle và truyền quy tắc cho fill()
transform của cha hoặc đường dẫnÁp dụng riêng phép biến đổi tương đương
CSS, gradient, mặt nạ, văn bản và ảnhTriển khai kết xuất cần thiết hoặc chọn cách khác

Mẫu dùng màu tô trực tiếp, hai phần tử đường dẫn là con trực tiếp của SVG và không có tính năng SVG khác. Nó cố ý nhỏ đủ để kiểm tra. Sao chép vòng lặp trích xuất vào công cụ nhập SVG bất kỳ sẽ bỏ mất kiểu kế thừa và các phần tử không hỗ trợ.

Đối chiếu nguồn với canvas chưa cấu hình

Biểu tượng tự tạo này có thân bo tròn xanh cổ vịt, mũi cam và lỗ vuông. viewBox bắt đầu tại (20, 10), không phải (0, 0). Đường dẫn con bên trong đi cùng chiều với ranh giới ngoài, nên quy tắc evenodd khai báo là quan trọng.

Đặt SVG này và một canvas vào trang HTML:

<svg id="source" xmlns="http://www.w3.org/2000/svg"
     viewBox="20 10 120 80" width="240" height="160">
  <path fill="#126d70" fill-rule="evenodd"
        d="M40 20 H90 Q110 20 110 40 V60 Q110 80 90 80 H40 Q30 80 30 70 V30 Q30 20 40 20 Z M50 35 H80 V60 H50 Z"/>
  <path fill="#f19b38" d="M115 25 L135 50 L115 75 Z"/>
</svg>
<canvas id="result"></canvas>

Nếu lặp qua hai giá trị d và gọi ctx.fill(new Path2D(d)) mà không đặt gì khác, hình học dùng tọa độ canvas cùng màu tô và quy tắc tô mặc định. Nó không tự có diện mạo của SVG. fill() trên canvas nhận cả đường dẫn và quy tắc evenodd hoặc nonzero rõ ràng; mặc định là nonzero (MDN fill()).

Đối chiếu trong trình duyệt giữa biểu tượng SVG, bản Path2D thô nhỏ màu đen bị tô kín lỗ và bản canvas hai màu được làm vừa đúng
Cùng các đường dẫn tạo bằng tay trong trình duyệt: SVG tham chiếu, Path2D thô và phép làm vừa rõ ràng với màu cùng evenodd được khôi phục. Bảng ghi kích thước bitmap lưu nội dung canvas cho bốn đầu vào kích thước và DPR.

Phương pháp: chúng tôi kết xuất SVG tạo bằng tay này trong trình duyệt, vẽ hai đường dẫn qua cả hai cách canvas và chạy lại mã công bố trên trang HTML riêng. Ảnh chụp là đầu ra trình duyệt thực tế. Nó minh họa xử lý tọa độ và màu tô, không phải chuyển đổi PerfectVector.

Ánh xạ viewBox vào canvas

Cách giữ tỷ lệ mặc định của SVG làm viewBox vừa bên trong khung nhìn và căn giữa. Đặc tả hệ tọa độ SVG định nghĩa ánh xạ đó. Với ví dụ phẳng này, tái tạo xMidYMid meet bằng một tỷ lệ đồng đều và phép tịnh tiến:

scale = min(cssWidth / viewBoxWidth, cssHeight / viewBoxHeight)
tx = (cssWidth - viewBoxWidth * scale) / 2 - viewBoxX * scale
ty = (cssHeight - viewBoxHeight * scale) / 2 - viewBoxY * scale

Các số hạng cuối loại bỏ gốc tọa độ khác không. Ở 240 × 160, scale là 2, tx là -40 và ty là -20. Ở 300 × 120, scale trở thành 1.5; viewBox làm vừa rộng 180 pixel có 60 pixel khoảng trống ngang ở mỗi bên. Vì vậy tịnh tiến là (30, -15) sau khi tính gốc nguồn.

Đặt tập lệnh sau sau đoạn HTML phía trên. Nó xử lý thuộc tính trực tiếp trên các đường dẫn của mẫu này, vẽ lại toàn bộ canvas và tách kích thước CSS khỏi kích thước bitmap lưu nội dung canvas:

const source = document.querySelector("#source");
const vb = source.viewBox.baseVal;
const layers = [...source.querySelectorAll("path")].map(el => ({
  path: new Path2D(el.getAttribute("d")),
  fill: el.getAttribute("fill"),
  rule: el.getAttribute("fill-rule") || "nonzero"
}));
 
function draw(canvas, cssWidth, cssHeight, dpr = window.devicePixelRatio || 1) {
  canvas.style.width = `${cssWidth}px`;
  canvas.style.height = `${cssHeight}px`;
  canvas.width = Math.round(cssWidth * dpr);
  canvas.height = Math.round(cssHeight * dpr);
  const ctx = canvas.getContext("2d");
  const scale = Math.min(cssWidth / vb.width, cssHeight / vb.height);
  const tx = (cssWidth - vb.width * scale) / 2 - vb.x * scale;
  const ty = (cssHeight - vb.height * scale) / 2 - vb.y * scale;
  const px = canvas.width / cssWidth;
  const py = canvas.height / cssHeight;
  ctx.setTransform(px * scale, 0, 0, py * scale, px * tx, py * ty);
  for (const layer of layers) {
    ctx.fillStyle = layer.fill;
    ctx.fill(layer.path, layer.rule);
  }
}
 
draw(document.querySelector("#result"), 240, 160);
 

Đặt canvas.width và canvas.height xóa và đặt lại ngữ cảnh. Hàm sau đó đặt phép biến đổi hoàn chỉnh và tô lại mọi lớp, nên gọi lặp không tích lũy tỷ lệ. Nó giả định viewBox hợp lệ với chiều rộng, cao dương và kích thước yêu cầu dương.

px và py dùng kích thước bitmap thực tế đã làm tròn. Điều đó giữ ánh xạ tọa độ CSS vào pixel được cấp ngay cả khi mật độ yêu cầu tạo kích thước phân số. Ví dụ devicePixelRatio của MDN giải thích vì sao cần xử lý riêng kích thước hiển thị canvas và kích thước pixel.

Kiểm tra riêng kích thước và mật độ pixel

Chúng tôi truyền rõ giá trị DPR 1 và 2 vào cùng hàm ở hai kích thước CSS. Đây là đầu vào mật độ được kiểm soát, không phải tuyên bố đã thử hai thiết bị vật lý. Tỷ lệ pixel thiết bị trình duyệt báo cho lần gọi mặc định là 2.

Kích thước CSSTham số DPRBitmap canvas quan sát được
240 × 1601240 × 160
240 × 1602480 × 320
300 × 1201300 × 120
300 × 1202600 × 240

Tâm lỗ vẫn trong suốt ở cả bốn trường hợp. Mật độ cao hơn thay đổi độ phân giải bitmap; không sửa phép biến đổi sai hay khôi phục quy tắc tô bị thiếu. Vẽ lại khi kích thước CSS dự định hoặc tỷ lệ pixel thiết bị thay đổi. Chỉ phóng lớn bằng CSS kéo giãn bitmap đã có trên canvas.

Nếu nguồn đã sai trước khi đến canvas, dùng kiểm tra kích thước và viewBox SVG. Nếu chỉ canvas sai, đối chiếu phép biến đổi và xử lý màu tô với SVG tham chiếu hoạt động đúng trước.

Giữ nguồn và chọn cách nhập phù hợp

Giữ bản gốc SVG và thuộc tính cùng mọi dữ liệu đường dẫn được tạo từ nó. Quy trình vẽ Path2D hữu ích khi bạn kiểm soát bộ đường bao nhỏ và kiểu của chúng. Với SVG phức tạp mà chỉ cần diện mạo trình duyệt hiển thị, tải dưới dạng ảnh rồi vẽ ảnh đó có thể tránh phải dựng lại mọi tính năng. Cách đó vẫn vẽ vào bitmap canvas.

Lưu đầu ra canvas thành PNG tạo ảnh raster, dù việc vẽ bắt đầu từ đường dẫn vector. API xuất canvas tạo blob ảnh; hỗ trợ PNG là bắt buộc (MDN toBlob()). Giữ SVG để sửa đường dẫn sau này. Tương tự, tệp SVG có thể chứa ảnh raster thay vì dữ liệu đường dẫn hữu ích; chẩn đoán ảnh nhúng giúp phân biệt các trường hợp.

PerfectVector phù hợp khi biểu tượng tùy chỉnh chỉ còn PNG hoặc JPG và bạn cần đường bao tái sử dụng. Chuyển ảnh đó thành SVG, rồi kiểm tra đường bao, các màu riêng và lỗ nhỏ trước khi trích đường dẫn cho canvas. Bản gốc SVG sạch nên đi thẳng vào bước chuẩn bị. Dò nét không triển khai ánh xạ viewBox, sao chép CSS hay làm PNG canvas chỉnh sửa được như vector.

Với cả họ biểu tượng, thiết lập lưới và quy tắc màu tô chung cho bộ giao diện trước khi nối từng quy trình vẽ. Các ví dụ chuẩn bị SVG khác nằm trong blog.

Trước khi dùng hình vẽ trong giao diện

  • Kiểm tra SVG tham chiếu ở cùng kích thước hiển thị với canvas.
  • Xác nhận gốc viewBox cùng chiều rộng và cao.
  • Chuyển màu tô và quy tắc tô dự định; kiểm tra mọi lỗ.
  • Tính phép biến đổi nhóm thay vì chỉ sao chép d.
  • Đọc canvas.width và canvas.height thực tế, rồi thử kích thước hiển thị lớn nhất dự định.
  • Giữ bản gốc SVG và cung cấp tên hỗ trợ trợ năng hoặc văn bản xung quanh phù hợp với giao diện.

Câu hỏi thường gặp

Path2D có thể nhập cả tệp SVG không? Không. Đầu vào chuỗi là dữ liệu đường dẫn SVG, như giá trị thuộc tính d của đường dẫn. Cấu trúc tài liệu, ánh xạ viewBox, kiểu và phép biến đổi nhóm cần xử lý riêng.

Vì sao lỗ trong SVG bị tô kín trên canvas? Kiểm tra quy tắc tô và chiều các đường dẫn con. Ví dụ này cần evenodd, còn fill trên canvas mặc định là nonzero. Truyền quy tắc dự định khi tô đối tượng Path2D.

Vẽ đường dẫn SVG trên canvas có giữ bản xuất vector không? Đường dẫn vẫn là chỉ dẫn vẽ tái sử dụng trong chương trình, nhưng xuất canvas thành PNG tạo pixel. Giữ SVG gốc để chỉnh sửa và bàn giao vector.

Nguồn tham khảo

  1. MDN — Hàm khởi tạo Path2D — Định nghĩa chuỗi dữ liệu đường dẫn hàm khởi tạo nhận.
  2. W3C — Hệ tọa độ SVG — Định nghĩa gốc viewBox, ánh xạ khung nhìn và căn chỉnh giữ tỷ lệ.
  3. MDN — Canvas fill() — Mô tả tô đường dẫn và quy tắc evenodd, nonzero.
  4. MDN — devicePixelRatio — Giải thích kích thước hiển thị CSS và độ phân giải bitmap của canvas.
  5. MDN — Canvas toBlob() — Mô tả xuất ảnh raster từ canvas.

Bạn chỉ có biểu tượng raster? Chuẩn bị SVG bằng PerfectVector, kiểm tra đường bao và lỗ, rồi đối chiếu hình vẽ canvas với SVG đó ở kích thước giao diện thực tế.

Thêm từ blog

Bắt đầu với SVG gọn sạch hơn
và dễ chỉnh sửa hơn