2013-07-16 81 views
6

Tôi đã tự hỏi liệu có thể chuyển đổi giá trị CSS tuyệt đối thành giá trị tương đối trong khi duy trì tỷ lệ bằng Javascript hay không. Giải pháp phải hoạt động với HTML cũng như đánh dấu SVG.Chuyển đổi giá trị tuyệt đối thành CSS tương đối bằng cách sử dụng Javascript

Giả sử bạn nhận điều này:

<div style="width:100%;height:800px"> 
    <svg width="1000" height="500"> 
    <rect width="30" height="50" x="34" y="24"/> 
    </svg> 
</div> 

Làm thế nào bạn sẽ đạt được điều này?

<div style="width:100%;height:100%"> 
    <svg width="83%" height="62.5%"> 
    <rect width="3%" height="10%" x="3.4% y="4.8%"/> 
    </svg> 
</div> 

tôi muốn

  1. bắt đầu tại phần tử gốc (div)
  2. được .width().height() của this và yếu tố phụ huynh
  3. tỷ lệ tính toán (this_height/parent_height*100)
  4. bộ chiều rộng và chiều cao phù hợp
  5. lặp lại đối với trẻ em, đặt con làm gốc, bắt đầu tại 1.

Những cạm bẫy phổ biến có thể xảy ra là gì? Có thể các thẻ không có chiều rộng hoặc chiều cao được đặt, như svg:g. Sau đó, có các định nghĩa kích thước không chuẩn như trong số svg:circle hoặc các thuộc tính mà bạn không nghĩ đến lúc đầu, chẳng hạn như svg:text s dy. Có cách nào để đoán một cách có học thức về hiệu suất đạt được do sự phản xạ lớn không? Còn gì nữa?

Cảm ơn bạn rất nhiều!

+0

này sẽ rất khó khăn vì nhiều lý do. Hãy suy nghĩ về thuộc tính '' và 'transform' của SVG cũng như các biến đổi CSS. – user123444555621

+0

Nếu tôi hiểu chính xác, tỷ lệ SVG của bạn sẽ được giải quyết bằng thuộc tính 'viewBox'. – Duopixel

+0

Mục đích cơ bản của việc này là gì? – screenmutt

Trả lời

1

Ok. Vì vậy, đây là những gì tôi hiểu rằng bạn muốn làm theo lời của riêng tôi.

Vòng qua tất cả các phần tử DOM chuyển đổi kích thước tuyệt đối (ví dụ: chiều cao/chiều rộng về mặt pixel) vào kích thước tương đối (ví dụ: chiều cao/chiều rộng về tỷ lệ phần trăm). Điều này nên được thực hiện với độ nhạy về việc liệu thứ nguyên được đặt bằng cách sử dụng CSS hay HTML thuộc tính để có thể sử dụng được cho các yếu tố SVG.

Điều đó đang được nói, có một hạn chế lớn: Nếu bạn muốn chuyển đổi kích thước tuyệt đối thành kích thước tương đối bằng cách sử dụng <x> làm phần tử gốc, thì <x> phải có thứ nguyên tuyệt đối. Nếu không, kích thước tương đối bên trong vùng chứa sẽ thay đổi kích thước dựa trên nội dung của chúng thay vì kích thước tổng thể của phụ huynh <x>.

Với quy định đó, những gì bạn đang yêu cầu là khá dễ dàng. Đây là thuật toán cần được sử dụng.

  1. Bắt đầu tại <body>
  2. Cho <body> kích thước cố định cây DOM
  3. Walk cho <body> cách làm như sau cho mỗi phần tử
    1. Nhận kích thước của phần tử
    2. Nhận kích thước của mẹ
    3. Chuyển đổi đến kích thước tương đối (tức là chiều rộng/chiều rộng của phụ huynh).
    4. Áp dụng kích thước tương đối

Để kiểm tra mã của tôi tôi đã sử dụng mã HTML sau đây. Nó có cả mã bạn bao gồm và một thể hiện trong đó một phần tử con lớn hơn phần tử cha của nó. Đây là một trường hợp tôi xác định là một điều kiện biên để kiểm tra.

<div style="width:100%;height:800px"> 
    <svg width="150" height="200" style="background: red;"> 
    <rect width="30" height="50" x="34" y="24"/> 
    </svg> 

    <div style="background: green; width: 50px; height: 50px;"> 
     <p style="background: teal; width: 100px; height: 20px;">Content</p> 
    </div> 
</div> 

Đây là JavaScript. Nó là khá tốt nhận xét và cung cấp đầu ra giao diện điều khiển khá. Đối với mã sản xuất, tôi khuyên bạn nên loại bỏ ghi nhật ký bảng điều khiển.

Hãy nhớ rằng mã không thực thi ngay lập tức, nhưng trong khoảng thời gian trễ là 2 giây. Bằng cách này, bạn có thể thấy cách DOM trông trước khi nó được sửa đổi.

Đối với những người không biết mã sau đây sẽ trả lại a nếu mã đó có mặt và không phải là false và b nếu không.

foo = a || b; 

Chúng tôi có thể viết lại như một trong hai bên dưới, nhưng điều này gọn gàng hơn.

foo = a ? a : b; 

if(a) 
    foo = a 
else 
    foo = b 

Đây là jsFiddle.

Và mã!

/** convertToRelative 
* Convert absolute width/height to relative. Should work for 
* Both HTML and SVG objects. Tries to use CSS attributes, falls 
* back on HTML attributes. 
*/ 
function convertToRelative(element) { 
    // Get Element and Parent 
    element = $(element); 
    parent = element.parent(); 

    // If there is no valid with, we need to use attributes 
    var useAttr = !element.width(); 

    // Get dimensions from css or attributes 
    var width  = element.width() || element.attr('width'); 
    var height = element.height() || element.attr('height'); 
    var pWidth = parent.width() || parent.attr('width'); 
    var pHeight = parent.height() || parent.attr('height'); 

    // Find relative dimensions 
    var relWidth = (width/pWidth) * 100.0; 
    var relHeight = (height/pHeight) * 100.0; 

    // Log info 
    console.log(element); 
    console.log("1: "+ parent.height() +", 2: "+ parent.attr('height')); 
    console.log("Width: "+ width +", Height: "+ height); 
    console.log("PWidth: "+ pWidth +", pHeight: "+ pHeight); 
    console.log("relWidth: "+ relWidth +", relHeight: "+ relHeight); 

    // Clear current stylings 
    element.removeAttr('width'); 
    element.removeAttr('height'); 

    // Apply relative dimensions 
    if (useAttr) { 
     element.attr('width', relWidth +'%'); 
     element.attr('height', relHeight +'%'); 
    } else { 
     element.width(relWidth +"%"); 
     element.height(relHeight +"%"); 
    } 
} 

/** walk 
* Walk through a DOM element and all its sub elements running 
* the convertToRelative function to convert absolute positions 
* to relative positions. 
* DOES NOT CONVERT THE FIRST ELEMENT, ONLY CHILDREN. 
*/ 
function walk(element) { 
    $(element).children().each(function() { 
     convertToRelative(this); 
     walk(this); 
    }); 
} 

/** fixBody 
* In order to play with relative dimensions for children of the 
* body, we need to fix the dimensions of the body. Otherwise it 
* will resize as we begin to use relative dimensions. 
*/ 
function fixBody() { 
    $('body').css('width', $('body').width() +"px"); 
    $('body').css('height', $('body').height() +"px"); 
} 

// Walk the body and all children on a 2 second delay. 
setTimeout(function() { 
    console.log('Running Conversion'); 
    fixBody() 
    walk('body'); 
    console.log('Conversion Finished'); 
}, 2000); 

Hãy cho tôi biết nếu đây không phải là điều bạn muốn hoặc bạn gặp sự cố!

Đối với câu hỏi của bạn

cạm bẫy phổ biến mà nhiều khả năng có thể xảy ra là gì?

Lỗ hổng phổ biến nhất sẽ là chuyển đổi không chính xác. Một cái gì đó sẽ đi sai trong việc chuyển đổi và kết quả DOM sẽ trông giống như đầu vào.

Có thể các thẻ không có chiều rộng hoặc chiều cao được đặt, như svg: g. Sau đó, có các định nghĩa kích thước không chuẩn như trong svg: vòng tròn hoặc các thuộc tính mà bạn không nghĩ đến lúc đầu, như svg: text dy.

Có các thẻ không có yếu tố width/height. Tuy nhiên, bằng cách sử dụng jQuery nên cung cấp cho chúng tôi một chút mạnh mẽ hơn trong bộ phận này.Tôi đã không làm việc với jQuery và SVG nhiều, nhưng có một plugin để hỗ trợ có sẵn nếu chúng ta gặp phải bất kỳ vấn đề nào với các yếu tố tối nghĩa hơn.

Có cách nào để đoán một cách có kiến ​​thức về hiệu suất đạt được do sự phản xạ lớn không?

Tôi tin rằng mã này sẽ chạy ở O(m * n) Ở đâu m = delay for a single reflown = number of nodes. Các Reflows nên có một sự thay đổi khá nhất quán bởi vì chúng ta đang chuyển đổi các phần tử lớn nhất trước khi nhỏ nhất. NHƯNG Tôi có thể đã được chứng minh sai trên tuyên bố cuối cùng đó.

Còn gì nữa?

Success!

+0

Cảm ơn bạn đã nỗ lực! Bạn có nghĩ thuật toán cơ bản của chúng tôi có thể được mở rộng để xử lý toàn bộ đặc tả SVG không? Tôi đang nói về những thứ như clipPaths, polylines, transformations (dịch và quy mô đặc biệt là từ phép quay nên độc lập) và vv. – prayerslayer

+0

Tôi chắc chắn rằng nó có thể. Tôi không quen với nó. – screenmutt

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