2012-10-03 54 views
8

Có cách nào để trích xuất đường dẫn từ (các) chữ cái trong html 5, sau đó lấy (x, y) tọa độ dọc theo đường dẫn đó để (các) chữ cái có thể được tạo thành bằng các vòng tròn dọc theo đường dẫn thư đó?Trích xuất đường dẫn từ canvas html văn bản

Tôi muốn thực hiện tọa độ x, y và áp dụng hình dạng tại vị trí của chúng sao cho nó giống với chuỗi văn bản ở định dạng "pixelated", theo sau là một số hiệu ứng động.

Bất kỳ lời khuyên nào về việc nhận được một số loại tọa độ x, y dọc theo một đường dẫn của các ký tự trên canvas sẽ rất tuyệt vời.

chỉnh sửa: Tôi chủ yếu cố gắng để tự động tạo ra các tọa độ để làm điều gì đó tương tự như sau: http://www.html5canvastutorials.com/labs/html5-canvas-google-bouncing-balls/

+0

thể trùng lặp của [Html5 nút giao thông văn bản canvas] (http://stackoverflow.com/questions/19954058/html5-canvas-text- giao lộ) –

Trả lời

8

Đây là một nhiệm vụ khó khăn để làm bằng tay bằng cách đặt trực quan hình tròn dọc theo con đường của một lá thư.

Thậm chí còn khó tự động hơn (tự động!) Mà không có sự can thiệp của con người.

Dưới đây là cách tự động sắp xếp vòng kết nối để tạo thành chữ cái.

Câu trả lời là trong 2 phần ...

  1. Tìm kiếm "letterform",

  2. Tạo vòng tròn để điền vào và phác thảo các letterform.

1. Phần cứng

Frederik De Bleser đã mã hóa một thư viện đẹp gọi opentype.js mà phải mất một file font .ttf và phân tích ra phác thảo hình tượng bất kỳ ký tự được chỉ định sử dụng các đường cong bậc hai trên vải: https://github.com/nodebox/opentype.js

2. Các chỉ là một phần hơi ít khó

Đối với mỗi chữ cái:

  • Tìm "nhiều" điểm trên mỗi đường cong bậc hai. Đây là thuật toán để tính [x, y] trên đường cong tại một khoảng T. T sẽ nằm trong khoảng từ 0,00 ở đầu đường cong đến 1,00 ở cuối đường cong. T sẽ không tạo ra khoảng cách đều nhau [x, y] dọc theo đường cong, do đó bạn sẽ cần phải bỏ mẫu (Vì vậy, "nhiều" có thể có nghĩa là 1000 giá trị của T trong khoảng từ 0,00 đến 1,00).

    function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) { 
        var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x; 
        var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y; 
        return({x:x,y:y}); 
    } 
    
  • Tìm góc tiếp xúc với góc của đường cong tại các điểm đó. (Về cơ bản tính toán những gì sẽ là một góc bên phải để đường cong). Bạn có thể làm điều đó với đạo hàm tiếp theo của công thức bậc hai:

    function quadraticBezierTangentAngle(t, p0, p2, p1) { 
        var tt = 1 - t; 
        var dx = (tt * p1.x + t * p2.x) - (tt * p0.x + t * p1.x); 
        var dy = (tt * p1.y + t * p2.y) - (tt * p0.y + t * p1.y); 
        return Math.tan(Math.atan2(dy,dx)); 
    } 
    
  • Bắt đầu từ lúc bắt đầu của đường cong, tính toán mỗi khoảng cách từ hiện tại [x, y] để tiếp theo [x, y].Bạn có thể làm điều này với lý Pythagore:

    var dx=nextX-currentX; 
    var dy=nextY-currentY; 
    var distance=Math.sqrt(dx*dx+dy*dy); 
    
  • De-lặp lại trong các mảng sao cho tất cả phần còn lại [x, y] yếu tố này là 1px xa từ [x, y] yếu tố trước đó. Bạn có thể làm điều này bằng cách điền vào mảng thứ hai với các giá trị từ đầu tiên trong đó parseInt(nextInOriginalArray - lastDistanceInNewArray)==1;

  • Quyết định bán kính cho vòng kết nối của bạn sẽ tạo thành từng chữ cái. Điều này thực sự khó hơn nó có vẻ. Đối với phông chữ "khối ô vuông", bạn có thể vẽ chữ "I" trên canvas. Sau đó tìm nạp tất cả các pixles bằng cách sử dụng getImageData. Tính chiều rộng của đột quỵ dọc "I" bằng cách tìm kiếm số lượng điểm ảnh mờ đục chạy theo chiều ngang ở giữa dọc của chữ cái. Đối với phông chữ khối ô vuông, var radius = horizontalOpaquePixelCount/2;. Đối với các phông chữ có độ rộng biến đổi đột biến, bạn sẽ phải sáng tạo. Có thể var radius = horizontalOpaquePixelCount/3; hoặc var radius = horizontalOpaquePixelCount/4;.

  • Lặp lại qua mảng điểm và xác định vòng kết nối mới mỗi radius*2 pixel. Bạn tính toán điểm trung tâm cho mỗi vòng tròn bằng cách sử dụng góc tiếp tuyến và lượng giác như thế này:

    var centerX = curvePointX + radius*Math.cos(tangentAngle); 
    var centerY = curvePointY + radius*Math.sin(tangentAngle); 
    
  • Trong khi tạo vòng tròn, tại một số điểm đường cong của bức thư sẽ quay lưng lại khi bản thân, vì vậy bạn phải kiểm tra mỗi vòng tròn mới mà bạn tạo để chắc chắn nó sẽ không chồng lên một vòng tròn hiện có. Bạn có thể tính toán xem một vòng tròn mới sẽ cắt nhau vòng kết nối hiện như thế này:

    var dx = newCircleCenterX - existingCircleCenterX; 
    var dy = newCircleCenterY - existingCircleCenterY; 
    var distance=Math.sqrt(dx*dx+dy*dy); 
    var circlesAreIntersecting=(distance<=newCircleRadius+existingCircleRadius); 
    

Tinh chỉnh: Gần một số điểm điểm cuối trên đường đi của bức thư, bạn sẽ thấy rằng một vòng tròn bán kính đầy đủ tới sẽ tràn ra khỏi dạng chữ. Nếu điều đó xảy ra, bạn có thể thu nhỏ bán kính của một số vòng tròn để phù hợp với dạng chữ. Nếu bạn hoàn toàn là bán kính cố định cho vòng kết nối, bạn có thể tính toán lại bán kính cố định của tất cả các vòng tròn dựa trên bán kính trung bình của tất cả các vòng tròn - bao gồm cả bán kính bạn muốn "thu nhỏ" để phù hợp với biểu mẫu.

Ví dụ: Đây là chữ "L hình thành bởi 15 vòng tròn.

enter image description here

Nhưng 2 vòng tròn màu đỏ rơi ra khỏi letterform của nó. Bạn có thể (1) thu nhỏ vòng tròn màu đỏ để phù hợp với bên trong letterform hoặc (2) tính toán lại một bán kính vòng tròn cố định mới dựa trên bán kính trung bình phù hợp với các letterform:

var total=0; 
total += greenRadii * 13; 
total += verticalRedRadiusResizedToFitInsideLetterform; 
total += horizontalRedRadiusResizedToFitInsideLetterform; 
var newRadius = total/15; 

Bạn có thể tính toán độ dài của bán kính đỏ mà sẽ phù hợp với những letterform bằng cách tính toán giao điểm của 2 đường: (1) các đoạn thẳng được hình thành bằng cách kết nối trung tâm vòng tròn màu xanh lá cây cuối cùng và trung tâm vòng tròn màu đỏ, (2) dòng được hình thành vuông góc với nhau m điểm cuối cùng trên đường cong. Dưới đây là một thuật toán để tính toán giao điểm của 2 dòng:

// Get interseting point of 2 line segments (if any) 
// Attribution: http://paulbourke.net/geometry/pointlineplane/ 
function line2lineIntersection(p0,p1,p2,p3) { 

    var unknownA = (p3.x-p2.x) * (p0.y-p2.y) - (p3.y-p2.y) * (p0.x-p2.x); 
    var unknownB = (p1.x-p0.x) * (p0.y-p2.y) - (p1.y-p0.y) * (p0.x-p2.x); 
    var denominator = (p3.y-p2.y) * (p1.x-p0.x) - (p3.x-p2.x) * (p1.y-p0.y);   

    // Test if Coincident 
    // If the denominator and numerator for the ua and ub are 0 
    // then the two lines are coincident.  
    if(unknownA==0 && unknownB==0 && denominator==0){return(null);} 

    // Test if Parallel 
    // If the denominator for the equations for ua and ub is 0 
    //  then the two lines are parallel. 
    if (denominator == 0) return null; 

    // If the intersection of line segments is required 
    // then it is only necessary to test if ua and ub lie between 0 and 1. 
    // Whichever one lies within that range then the corresponding 
    // line segment contains the intersection point. 
    // If both lie within the range of 0 to 1 then 
    // the intersection point is within both line segments. 
    unknownA /= denominator; 
    unknownB /= denominator; 

    var isIntersecting=(unknownA>=0 && unknownA<=1 && unknownB>=0 && unknownB<=1) 

    if(!isIntersecting){return(null);} 

    return({ 
     x: p0.x + unknownA * (p1.x-p0.x), 
     y: p0.y + unknownA * (p1.y-p0.y) 
    }); 
} 
+0

bạn có biết thực hiện bất kỳ thuật toán nào mà bạn đã mô tả không? – seltzlab

+0

Đó là một ứng dụng nhiều hơn một thuật toán - một loạt các thuật toán. Tôi có khoảng 75% ứng dụng này. Tôi không có thời gian để hoàn thành nó. :-) – markE

10

Pixel rộng

Một phương pháp đơn giản này là để làm như sau:

  • Sử dụng một phông chữ nhỏ, rút ​​ra những văn bản sử dụng màu đồng nhất
  • Lặp lại tất cả các pixel.Bất kỳ pixel nào có alpha = 255, lưu trữ vào một mảng nhưng với x và y được chia tỷ lệ với đường kính

Bây giờ bạn có một loạt các "quả bóng" đại diện cho văn bản và có thể hoạt hình. Nó không phải là siêu chính xác khi nói đến khoảng cách chữ nhưng nó phải làm cho mục đích đã cho (bạn luôn có thể đo từng chữ cái và tại điểm tách tăng giá trị x kết thúc bằng cách sử dụng giá trị delta bổ sung).

Kích thước phông chữ lớn hơn có thể cải thiện chất lượng nhưng cũng sẽ tạo ra nhiều điểm hơn. Một kiểu phông chữ khác với kiểu chung được sử dụng trong bản trình diễn bên dưới cũng có thể mang lại lợi ích cho giao diện tổng thể (thử nghiệm!). Bạn cũng có thể tinh chỉnh giá trị ngưỡng alpha để bao gồm các pixel không hoàn toàn vững chắc, nhưng có ảnh hưởng.

Và cuối cùng, các trình duyệt khác nhau hiển thị văn bản khác nhau để bạn có thể muốn ghi nhớ điều đó (xem phần trên để đo từng chữ cái để thêm khoảng trắng giữa chúng).

Demo

snapshot

var ctx = document.querySelector("canvas").getContext("2d"), 
 
    inp = document.querySelector("input"), 
 
    w = ctx.canvas.width, 
 
    h = ctx.canvas.height, 
 
    balls = [];          // global ball array 
 

 
ctx.fillStyle = "rgb(0, 154, 253)";     // fill must be a solid color 
 
generate(inp.value)         // init default text 
 
inp.onkeyup = function() {generate(this.value)}; // get some text to demo 
 

 
function generate(txt) { 
 
    var i, radius = 5,        // ball radius 
 
     data32;          // we'll use uint32 for speed 
 
    
 
    balls = [];          // clear ball array 
 
    ctx.clearRect(0, 0, w, h);      // clear canvas so we can 
 
    ctx.fillText(txt.toUpperCase(), 0, 10);   // draw the text (default 10px) 
 
    
 
    // get a Uint32 representation of the bitmap: 
 
    data32 = new Uint32Array(ctx.getImageData(0, 0, w, h).data.buffer); 
 
    
 
    // loop through each pixel. We will only store the ones with alpha = 255 
 
    for(i = 0; i < data32.length; i++) { 
 
    if (data32[i] & 0xff000000) {    // check alpha mask 
 
     balls.push({       // add new ball if a solid pixel 
 
     x: (i % w) * radius * 2 + radius,  // use position and radius to 
 
     y: ((i/w)|0) * radius * 2 + radius, // pre-calc final position and size 
 
     radius: radius, 
 
     a: (Math.random() * 250)|0   // just to demo animation capability 
 
     }); 
 
    } 
 
    } 
 
    // return array - here we'll animate it directly to show the resulting objects: 
 
} 
 

 
(function animate() { 
 
    ctx.clearRect(0, 0, w, h); 
 
    ctx.beginPath(); 
 
    for(var i = 0, ball; ball = balls[i]; i++) { 
 
    var dx = Math.sin(ball.a * 0.2) + ball.radius, // do something funky 
 
     dy = Math.cos(ball.a++ * 0.2) + ball.radius; 
 
    ctx.moveTo(ball.x + ball.radius + dx, ball.y + dy); 
 
    ctx.arc(ball.x + dx, ball.y + dy, ball.radius, 0, 6.28); 
 
    ctx.closePath(); 
 
    } 
 
    ctx.fill(); 
 
    requestAnimationFrame(animate); 
 
})();
body {font:bold 16px sans-serif}
<label>Type some text: <input value="PIXELS"></label><br> 
 
<canvas width=1024></canvas>

Các vấn đề liên quan