基於 CSS3 Media Queries 的 HTML5 應用

先來介紹下 media,確切的說應該是 CSS media queries(CSS 媒體查詢),媒體查詢包含了一個媒體類型和至少一個使用如寬度、高度和顏色等媒體屬性來限制樣式表範圍的表達式。CSS3 加入的媒體查詢使得無需修改內容即可以使樣式應用於某些特定的設備範圍。 css

那麼該怎麼定義 media 呢,看下面的代碼,你確定能猜出個大概。html

<!-- link元素中的CSS媒體查詢 -->
<link rel="stylesheet" media="(max-width: 800px)" href="example.css" />

<!-- 樣式表中的CSS媒體查詢 -->
<style>
@media (max-width: 600px) { .facet_sidebar { display: none; } } </style>

關於解釋,文檔中是這麼說的,當媒體查詢爲真時,相關的樣式表或樣式規則就會按照正常的級聯規則被應用。即便媒體查詢返回假, <link> 標籤上帶有媒體查詢的樣式表仍將被下載(只不過不會被應用)。node

因此呢,這也是一種弊端,若是說對某個頁面定義了多個樣式標準來因對不一樣的 media 屬性的話,那在頁面的加載時間將會受到影響,可是話有說回來,在當前網絡快速發展的時代,網速也在不斷地完善和提升,所以影響並不大,幾乎能夠忽略不計。web

media 還能夠經過邏輯操做符(and、not、only 等)來組成 media 表達式,書寫更復雜的過濾條件,這些表達式我就再也不這邊一一說明了,想深刻了解的同窗,能夠閱讀相關的說明文檔:https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries 這裏面有作詳細的介紹。算法

接下來咱們來用幾個 Demo 來演示下 media 的用法及表現。express

既然咱們今天的目的是探討如何監聽 devicePixelRatio 屬性的變化,那麼咱們就以在不一樣的 devicePixelRatio 值狀況下,來改變某個 div 的 background 樣式,具體的代碼以下:瀏覽器

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <style media="screen">
            @media screen and (min-resolution: 2dppx) { #image { background : red; } } @media screen and (min-resolution: 1dppx) { #image { background: #000; } } </style> </head> <body> <div id="image" style="width:100px; height:100px"></div> </body> </html>

代碼有了,那麼要怎麼測試呢?在通常狀況下,devicePixelRatio 屬相是不會變化的,可是確定會存在特殊狀況的,就好比說,你的電腦接了兩個顯示器,並且兩臺瀏覽器的 devicePixelRatio 屬性是不同的,那麼恭喜你,你已經具有測試條件,只須要將頁面從一個屏拖到另一個屏,這樣你就能夠看到效果了。網絡

有去測試的同窗會發現,div 的背景色並無想代碼中設置的那樣,在不一樣的 devicePixelRatio 屬性值下,展示出不一樣的顏色,這是爲何呢?ide

這代碼是我最開始寫代碼,運行後發現沒效果,起初我也不知道緣由,在跨屏拖動頁面的時候,在瀏覽器控制檯中,我找到了緣由。那麼究竟是什麼緣由致使設置無效的呢?咱們來看看兩個屏幕下的 Style 內容截圖,左邊是 min-resolution 等於 1,右邊是等於 2工具

       

對比着兩個圖,能夠發現,在 min-resolution 等於 2 的狀況下,在裏面定義的屬性被覆蓋掉了,並無生效,這是爲何呢?

要解釋的話,這裏恐怕須要補充一點知識,就是關於 min- 和 max- 的前綴,在代碼中的所起到的具體效果,在文檔中是這麼描述的:大多數媒體屬性帶有 「min-」 和 「max-」 前綴,用於表達 「大於等於」 和 「小於等於」。這避免了使用與HTML和XML衝突的 「<」 和 「>」 字符。若是你未向媒體屬性指定一個值,而且該特性的實際值不爲零,則該表達式被解析爲真。若是瀏覽器運行的設備上沒有該屬性值,包含這個屬性值的表達式通常返回假。

其實上面的說明已經幫我解釋清楚了,我再通俗地和你們解釋一下:當 devicePixelRatio 爲 1 時,只有 min-resolution: 1dppx 這個條件知足,所以 div 的顏色是黑色沒錯;當 devicePixelRatio 爲 2 時,兩個 media 都知足條件,同時 CSS 的規則是後加載的樣式將會覆蓋先加載的樣式,因爲我麼將 min-resolution: 1dppx 的 media 寫在後面,所以若是兩個 media 都知足條件的話, min-resolution: 1dppx 的 media 將會覆蓋 min-resolution: 2dppx 的 media,所以無論你把頁面拖到那個屏幕,那個 div 的背景色都是黑色。

那麼咱們將兩個 media 調換一下位置,問題就順利地解決了。

<style media="screen">
    @media screen and (min-resolution: 1dppx) { #image { background: #000; } } @media screen and (min-resolution: 2dppx) { #image { background : red; } } </style>

以上是根據不一樣的 media 條件設置不一樣的樣式,這是 CSS 的作法,在 JavaScript 中,沒有專門的方法來監聽 window.devicePixelRatio 屬性變化,那麼該怎麼監聽 devicePixelRatio 屬性的變化呢?方法也很簡單,看看下面的代碼,你必定就明白了:

window.matchMedia('screen and (min-resolution: 2dppx)').addListener(function(e) {
    console.info(e, window.devicePixelRatio);
});

稍微解釋下,經過 window.matchMedia(‘media expression’) 方法獲取到對應的 media,而後經過 addListener(function(e) {}) 來監聽 media 的變化。

有玩過 Canvas 的朋友必定知道,要想繪製出來的內容效果最佳的話,Canvas 自身的 width 和 height 屬性值與 style 中的 width 和 height 的比例應該剛好等於 devicePixelRatio 的值,全部若是你在切換不一樣 devicePixelRatio 屬性值的屏幕時,沒有從新設置 Canvas 的寬高的話,繪製出來的畫面將不是最佳的效果。

接下來咱們基於 HT for Web 的 3D 模型來作一個小實驗。實驗的內容是這樣的,在 GraphView 中有一輛車根據某條路線前行,當拖到另一個屏幕的時候,換輛車子。先來看看效果圖:

上面兩張圖分別是在不一樣的屏幕中的截圖,車子動起來的效果能夠訪問如下連接:

實驗的地址是:http://www.hightopo.com/demo/media/index.html 如下是實驗的具體代碼:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>HT for Web</title>
        <style media="screen">
            @media screen and (min-resolution: 2dppx) {} html, body { padding: 0px; margin: 0px; } </style> <script src="../../oldhtforweb/lib/core/ht.js"></script> <script src="../../oldhtforweb/lib/plugin/ht-modeling.js"></script> <script src="../../oldhtforweb/lib/plugin/ht-obj.js"></script> <script> ht.Default.setImage('road', './images/road.jpg'); var init = function() { g3d = new ht.graph3d.Graph3dView(); var dm = g3d.dm(); g3d.addToDOM(); g3d.setEye(1200, 300, 0); g3d.getNote = function(data) { if (data.getTag() !== 'carNode') return null; return 'DevicePixelRatio : ' + window.devicePixelRatio; }; var carIndex = 0; window.matchMedia('screen and (min-resolution: 2dppx)').addListener(function() { carIndex = (carIndex + 1) % 2; var obj = result[carIndex]; carNode.s('shape3d', obj.name); ht.Default.setDevicePixelRatio(); }); var polyline = createPath(dm, 300), params = { delay: 0, duration: 10000, easing: function(t){ return (t *= 2) < 1 ? 0.5 * t * t : 0.5 * (1 - (--t) * (t - 2)); }, action: function(v, t){ var length = g3d.getLineLength(polyline); var offset = g3d.getLineOffset(polyline, length * v), point = offset.point, px = point.x, py = point.y, pz = point.z, tangent = offset.tangent, tx = tangent.x, ty = tangent.y, tz = tangent.z; carNode.p3(px, py - 9, pz); carNode.lookAt([px + tx, py + ty - 9, pz + tz], 'front'); }, finishFunc: function(){ ht.Default.startAnim(params); } }, carList = [ 'fordFocus', 'concept-sedan-01v2'], result = [], carNode = new ht.Node(); carNode.setTag('carNode'); carList.forEach(function(name, index) { ht.Default.loadObj('./objs/'+name+'/'+name+'.obj', './objs/'+name+'/'+name+'.mtl', { cube: true, center: true, shape3d: name, finishFunc: function(modelMap, array, rawS3) { var k = 110 / rawS3[0]; rawS3 = rawS3.map(function(v) { return v * k; }); result[index] = { 'name' : name, 'modelMap' : modelMap, 'array' : array, 'rawS3' : rawS3 }; if (index === 0) { var node = carNode; node.s({ 'wf.width' : 0, 'shape3d' : name, 'note.position' : 44, 'note' : 'DevicePixelRatio : ' + window.devicePixelRatio, 'note.face' : 'top', 'note.autorotate' : true, 'note.font' : '46px arial, sans-serif' }); node.s3(rawS3); node.r3(0, Math.PI, 0); dm.add(node); polyline.setElevation(rawS3[1] * 0.5 + 2); ht.Default.startAnim(params); } } }); }); }; var createPath = function(dm, radius) { var polyline = new ht.Polyline(); polyline.setThickness(2); polyline.s({ 'shape.border.pattern': [16, 16], 'shape.border.color': 'rgba(0, 0, 0, 0)', 'shape3d.resolution': 300, '3d.selectable': false }); dm.add(polyline); var cx = 0, cy = radius * Math.PI * 0.5, count = 500, points = [{ x: radius, y: -cy, e: 0 }], segments = [1]; for (var k = 0; k < count + 1; k++) { var angle = k * Math.PI / count; points.push({ x: cx + radius * Math.cos(angle), y: cy + radius * Math.sin(angle), e: 0 }); segments.push(2); } cy *= -1; radius *= -1; for (var k = 0; k < count + 1; k++) { var angle = k * Math.PI / count; points.push({ x: cx + radius * Math.cos(angle), y: cy + radius * Math.sin(angle), e: 0 }); segments.push(2); } polyline.setPoints(points); polyline.setSegments(segments); var shape = new ht.Shape(); shape.setPoints(points); shape.setSegments(segments); shape.s({ 'top.visible' : false, 'bottom.image' : 'road', 'bottom.reverse.flip' : true, 'bottom.uv.scale' : [13, 1], 'back.visible' : false, 'front.reverse.flip' : true, '3d.selectable': false }); shape.setThickness(180); shape.setTall(15); shape.setClosePath(true); dm.add(shape); return polyline; }; </script> </head> <body onload="init();"> </body> </html>

來介紹下此次 Demo 中都用到的了 HT for Web 的那些技術。

首先是車子,車子並非經過 HT for Web 生成的,而是經過專業的 3D 工具設計,而後導出 obj 和 mtl 文件,HT for Web 對 obj 和 mtl 文件進行解析,而後顯示在 Graph3dView 中,更多具體的介紹能夠查閱我麼的 obj 文檔:http://www.hightopo.com/guide/guide/plugin/obj/ht-obj-guide.html

在 obj 文檔中,你會看到一個一個飛機的例子,飛機沿着設定好的路線飛行,你應該會想,這個尋路是怎麼實現的呢?其實很簡單,咱們將路線切割成一個個很小很小的單元,而後根據算法依次獲取到小單元的座標設置到移動的物體上,這樣物體就動起來了。 

在 Demo 中,有一條很精緻的馬路,這條馬路就是一個 Shape 節點,根據車的路徑生成的馬路,Shape 是一個六面體,由於首尾相連了,因此沒有左右面,在這個例子中,我將馬路的 back 和 top  面隱藏了,而後 bottom 面支持翻轉,讓 bottom 面的貼圖顯示在內表面上,這樣馬路就建成了。

相關文章
相關標籤/搜索