ẢNH NINE-PATCH – SCALE KHÔNG VỠ TRONG JAVASCRIPT

Có thể bạn đã từng nghe đến ảnh Vector rồi. Ảnh Vector có một ưu điểm lớn nhất là có thể kéo to, nhỏ tùy ý mà không bị vỡ ảnh. Bên cạnh đó, có một loại ảnh khác cũng có ưu điểm tương tự, chính là ảnh nine-patch (hay 9patch). Vậy ảnh nine-patch là gì? Cách sử dụng ảnh nine-patch trong JavaScript như thế nào? Bài viết này sẽ giúp bạn tìm hiểu về xử lý ảnh nine-patch trong JavaScript.

Ảnh nine-patch là gì?

Ảnh nine-patch (hay 9-patch, tạm dịch là 9 miếng vá) là ảnh có thể thay đổi kích thước mà không bị vỡ ảnh. Android sẽ tự động thay đổi kích thước của ảnh để tương thích với View. Theo như định nghĩa trên, ảnh nine-patch được tạo ra bởi Android (không rõ). Ảnh nine-patch thực chất là một ảnh PNG có cấu tạo đặc biệt hơn với border 1-pixel để xác định phần co dãn (stretchable), phần cố định (static) và phần chứa nội dung (drawable). Ngoài ra, ảnh nine-patch có tên dạng: .9.png

Cấu tạo ảnh nine-patch

Phần co dãn, cố định

Theo định nghĩa ở trên, ảnh nine-patch sẽ sử dụng border có độ rộng 1 pixel để xác định phần co dãn và phần cố định của ảnh. Trong đó, phần co dãn được xác định bởi 2 đường kẻ màu đen ở phía trên (top) và phía bên trái (left), những pixel còn lại ở top và left phải để transparent hoặc màu trắng hoàn toàn. nine-patch button stretchable 0- complete javascript - completejavascript.com Hai đường kẻ này chia bức ảnh thành 9 phần như sau: nine-patch button stretchable 2- complete javascript - completejavascript.com Trong 9 phần đó:

  • Số 1, 3, 7 và 9 là các phần cố định
  • Số 2 và 8 là các phần được kéo dãn theo chiều ngang (horizontal)
  • Số 4 và 6 là các phần được kéo dãn theo chiều dọc (vertical)
  • Số 5 là phần được kéo dãn theo 2 chiều (cả ngang và dọc)

Phần chứa nội dung

Ngoài việc định nghĩa phần co dãn và phần cố định của ảnh, ta có thể định nghĩa phần chứa nội dung, tương đương với việc định nghĩa padding. Phần này được xác định bởi 2 đường kẻ màu đen ở phía bên phải (right) và phía dưới (bottom). nine-patch button stretchable 3- complete javascript - completejavascript.com Nghĩa là chỉ có hình chữ nhật màu hồng được phép chứa nội dung. Nếu nội dung lớn hơn vùng này thì ảnh nine-patch sẽ được kéo dãn ra để chứa đủ nội dung đó.

Tạo ảnh nine-patch

Bạn có thể dùng hầu hết các công cụ chỉnh sửa ảnh để tạo ảnh nine-patch: Photoshop, GIMP, ... Ngoài ra, bạn cũng có thể dùng các công cụ chuyên biệt như WYSIWYG của Android Studio hoặc Simple nine-patch generator.

Áp dụng ảnh nine-patch cho web

Để đơn giản, ta sẽ không quan tâm đến padding (padding = 0), nghĩa là phần đường kẻ màu đen phía dưới và bên phải kéo dài đến hết bức ảnh (trừ đi 1 pixel là border). nine-patch image demo - complete javascript - completejavascript.com Để scale ảnh nine-patch, tôi sẽ xử dụng Canvas với ý tưởng chung như sau:

  • Sử dụng canvas để xác định đường kẻ màu đen phía trên và bên trái,
  • Từ đó, suy ra phần cố định và phần được co dãn,
  • Vẽ lại ảnh lên 1 canvas khác,
  • Chuyển canvas thành dạng dataURL để sử dụng.

Sau một thời gian nghiên cứu, tôi đã viết lại thành một thư viện đơn giản là NinePatch.js. Tuy nhiên, thư viện này vẫn chưa hoàn thiện và chỉ có thể xử lý được những ảnh nine-patch có cấu tạo đơn giản. Các API mà bạn có thể áp dụng:

Hàm khởi tạo

NinePatch(srcImage, newWidth, newHeight) Trong đó:

  • srcImage: (String) url của ảnh
  • newWidth: (Number) chiều rộng của ảnh mới
  • newHeight: (Number) chiều dài của ảnh mới

Các hàm Setter

  • setSrcImage(srcImage)
  • setWidth(newWidth)
  • setHeight(newHeight)

Các hàm này dùng để cài đặt lại các thông số url, chiều rộng và chiều dài của ảnh khi bạn muốn thay đổi.

Hàm getSize

Hàm này trả về một Promise. Trong trường hợp thành công thì kết quả là một object với 3 thông số là url, width, height của bức ảnh gốc, như sau:

{url: dataUrl, width: image.width, height: image.height}

Hàm run

Cũng tương tự như hàm getSize nhưng hàm này sẽ trả về object là thông tin của ảnh mới sau khi scale. Để hiểu hơn về cách sử dụng NinePatch.js, mời các bạn xem ví dụ sau đây:

Ví dụ sử dụng NinePatch.js

const srcImg = 'https://res.cloudinary.com/drcrre4xg/image/upload/v1516665655/test_normal.9_gjksbl.png';
const WIDTH = 150;
const HEIGHT = 150;

var ninePatch;

window.onload = function() {
  ninePatchWorker = new NinePatch(srcImg, WIDTH, HEIGHT);
  view();
  normal();
  test();
};
/*
* Show nine patch image 
*/
function view() {
  var ninePatchImgDiv = document.getElementById('ninePatchImg');
  ninePatchWorker
  .getSize()
  .then(
    result => setImage(ninePatchImgDiv, result.url, result.width, result.height), 
    error => console.log('Get size of image error: ', error)
  );
}

/**
 * Show image after scaling without handling nine-patch image
 */
function test() {
  var testImgDiv = document.getElementById('testImg');
  ninePatchWorker
  .getSize()
  .then(
    result => setImage(testImgDiv, result.url, result.width + 50, result.height + 100), 
    error => console.log('Get size of image error: ', error)
  );
}

/*
* Show normal image after scaling nine-patch image
*/
function normal() {
  var normalImgDiv = document.getElementById('normalImg');
  ninePatchWorker
  .run()
  .then(result => setImage(normalImgDiv, result.url, result.width, result.height))
  .catch(error => {
    console.log('Error: ', error);
  });
}

function setImage(divElement, srcURL, width, height) {
  divElement.style.width = width + 'px';
  divElement.style.height = height + 'px';
  divElement.style.backgroundSize = '' + width + 'px ' + height + 'px'; 
  divElement.style.backgroundImage = "url('" + srcURL + "')";
}
  • Đầu tiên, tôi sẽ khởi tạo một đối tượng NinePatch:
const srcImg = 'https://res.cloudinary.com/drcrre4xg/image/upload/v1516665655/test_normal.9_gjksbl.png';
const WIDTH = 150;
const HEIGHT = 150;

var ninePatch;

window.onload = function() {
  ninePatchWorker = new NinePatch(srcImg, WIDTH, HEIGHT);
  view();
  normal();
  test();
};
  • Để hiển thị ảnh gốc tôi sẽ dùng hàm getSize(), nếu bạn chưa biết gì về Promise thì có thể tham khảo tại đây.
function view() {
  var ninePatchImgDiv = document.getElementById('ninePatchImg');
  ninePatchWorker
  .getSize()
  .then(
    result => setImage(ninePatchImgDiv, result.url, result.width, result.height), 
    error => console.log('Get size of image error: ', error)
  );
}
  • Ngoài ra, tôi sẽ thử nghiệm việc scale ảnh mà không quan tâm đến đó là ảnh nine-patch hay không
function test() {
  var testImgDiv = document.getElementById('testImg');
  ninePatchWorker
  .getSize()
  .then(
    result => setImage(testImgDiv, result.url, result.width + 50, result.height + 100), 
    error => console.log('Get size of image error: ', error)
  );
}
  • Cuối cùng để áp dụng thông tin ảnh nine-patch để scale, tôi sẽ dùng hàm run():
function normal() {
  var normalImgDiv = document.getElementById('normalImg');
  ninePatchWorker
  .run()
  .then(result => setImage(normalImgDiv, result.url, result.width, result.height))
  .catch(error => {
    console.log('Error: ', error);
  });
}

  • Hàm setImage() áp dụng DOM để set lại giá trị width, height, url cho thẻ div hiển thị bức ảnh.
function setImage(divElement, srcURL, width, height) {
  divElement.style.width = width + 'px';
  divElement.style.height = height + 'px';
  divElement.style.backgroundSize = '' + width + 'px ' + height + 'px'; 
  divElement.style.backgroundImage = "url('" + srcURL + "')";
}

Trên đây là những kiến thức cơ bản về ảnh nine-path, cũng như cách xử lý ảnh nine-patch trong JavaScript. Tuy nhiên, còn rất nhiều thứ cần phải cải tiến. Vì vậy, tôi rất mong nhận được sự góp ý từ các bạn. Xin chào và hẹn gặp lại bạn ở bài viết tiếp theo trong series JavaScript nâng cao. Thân ái,

Tham khảo


Theo dõi Lam Pham trên Kipalog để nhận thông báo khi có bài viết mới nhất:

Demo

.

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Lam Pham

23 bài viết.
41 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
25 10
Trước khi vào nội dung bài viết. Tôi xin đính chính giúp anh trả lời những câu hỏi chỉ là tựa đề của một bài hát tôi không có ý xưng hô là anh. Bài...
Lam Pham viết 7 tháng trước
25 10
White
13 6
Có thể bạn thừa biết, JavaScript là một ngôn ngữ chạy (Link). Điều đó có nghĩa là nếu bạn thực hiện một tác vụ quá lớn trên giao diện chính thì khả...
Lam Pham viết 7 tháng trước
13 6
White
13 2
Xin chào bạn Có thể bạn đã biết, JavaScript là một ngôn ngữ lập trình rất mạnh, nhưng lại vô cùng rắc rối, phức tạp. Để có thể nắm vững được Java...
Lam Pham viết 3 tháng trước
13 2
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
23 bài viết.
41 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!