canvas星空和圖形變換

来源:https://www.cnblogs.com/starof/archive/2018/03/27/8626422.html
-Advertisement-
Play Games

圖形變換。 一、畫一片星空 先畫一片canvas.width寬canvas.height高的黑色星空,再畫200個隨機位置,隨機大小,隨機旋轉角度的星星。 window.onload=function(){ var canvas=document.getElementById("canvas"); ...


圖形變換。

一、畫一片星空

先畫一片canvas.width寬canvas.height高的黑色星空,再畫200個隨機位置,隨機大小,隨機旋轉角度的星星。

window.onload=function(){
    var canvas=document.getElementById("canvas");

    canvas.width=800;
    canvas.height=800;

    var context=canvas.getContext("2d");
    
    context.fillStyle="black";
    context.fillRect(0,0,canvas.width,canvas.height);

    for(var i=0;i<200;i++){
        var r=Math.random()*10+10;
        var x=Math.random()*canvas.width;
        var y=Math.random()*canvas.height;
        var a=Math.random()*360;
        drawStar(context,x,y,r,r/2.0,a);
    }
    
}        




//rot順時針旋轉的角度
function drawStar(ctx,x,y,r,R,rot){
    ctx.beginPath();
    //角度轉弧度:除以180*PI
    for(var i=0;i<5;i++){
        ctx.lineTo(Math.cos((18+i*72-rot)/180*Math.PI)*R+x,
            -Math.sin((18+i*72-rot)/180*Math.PI)*R+y);
            ctx.lineTo(Math.cos((54+i*72-rot)/180*Math.PI)*r+x,
            -Math.sin((54+i*72-rot)/180*Math.PI)*r+y);
    }

    ctx.closePath();

    ctx.fillStyle="#fb3";
    ctx.strokeStyle="#fd5";
    ctx.lineWidth=3;
    ctx.lineJoin="round";

    ctx.fill();
    ctx.stroke();
}
View Code

 產生一個扁平化設計中200個星星的效果。

二、圖像變換和狀態保存

1、用標準路徑+圖形變換思想重構

上面drawStar函數承載的功能太多來,整個繪製路徑的指定,同時把五角星的位移,大小,旋轉多少度全部揉合在一個函數里了。

假如需要變為畫一個四角形?六角形?代碼改起來就比較麻煩了。

標準做法:修改函數結構。

介面不變,省去了旋轉角度,畫一個標準星星。假設外圓半徑是內圓半徑的兩倍,所以只需要傳入一個小r。drawStar里調用一個startPath()函數來繪製一個標準五角星的路徑。

標準的五角星路徑:只傳入一個context,在(0,0)的位置繪製來一個大圓半徑為1,同時沒有任何偏移,任何旋轉的的五角星。

在drawStar里勾繪出標準五角星後再通過圖形變換使得標準五角星的位移變成在(x,y)的位置,大小變成R這麼大,同時旋轉rot角度。再進行具體的繪製。

這樣一個設計的結構可以避免之前的問題。比如需求變成要畫六角形,四角形,只需要把starPath()裡面路徑勾繪的代碼進行相應的更改即可。

更高級的復用:starPath()函數以參數的形式傳入drawStar()中。這樣drawStar可以叫drawSheap用戶可以繪製任意的圖形,只需要傳入繪製圖形的標準路徑,變更的位移量,大小量,旋轉量即可。 

//rot順時針旋轉的角度
function drawStar(ctx,x,y,r,R,rot){
    starPath(ctx);
    //繪製在(x,y)大小為R,旋轉rot度的五角星
    //...
}
function starPath(ctx){ ctx.beginPath(); //角度轉弧度:除以180*PI for(var i=0;i<5;i++){ ctx.lineTo(Math.cos((18+i*72)/180*Math.PI), -Math.sin((18+i*72)/180*Math.PI)); ctx.lineTo(Math.cos((54+i*72)/180*Math.PI), -Math.sin((54+i*72)/180*Math.PI)); } ctx.closePath(); }

總結:圖形學里繪製先繪製標準路徑,再通過圖形變換成需求大小。

2,圖形變換

三種基本操作:

  • 位移translate(x,y)
  • 旋轉rotate(deg)
  • 縮放 scale(sx,sy)

translate會疊加

綠色正方形位置經過2次translate後到達了(200,200)。並不是代碼里看起來的(150,150)。

window.onload=function(){
    var canvas=document.getElementById("canvas");

    canvas.width=400;
    canvas.height=400;

    var context=canvas.getContext("2d");

    context.fillStyle="red";
    context.translate(50,50);
    context.fillRect(0,0,200,200);

    context.fillStyle="green";
    context.translate(150,150);
    context.fillRect(0,0,200,200);
}  
View Code

為了避免上述問題,最佳實踐是使用圖形變換之後,再反向操作把圖形變換的結果逆轉過來。如下:

window.onload=function(){
    var canvas=document.getElementById("canvas");

    canvas.width=400;
    canvas.height=400;

    var context=canvas.getContext("2d");

    context.fillStyle="red";
    context.translate(50,50);
    context.fillRect(0,0,200,200);
    context.translate(-50,-50);//反向操作

    context.fillStyle="green";
    context.translate(150,150);
    context.fillRect(0,0,200,200);
    context.translate(-150,-150);//反向操作
}  
View Code

3,canvas狀態的保存save()和恢復restore()

逆轉圖形變換太麻煩了,canvas提供了一個save()API,保存當前的圖形狀態,狀態包括所有我們設置的狀態,自然也包括圖形變換的狀態。

在完成圖形變換並且具體繪製以後,在最後再調用一次context.restore()。

restore()和save()是成對出現的,restore()返回在save()時候canvas的所有狀態, 這是一個非常好的保持canvas繪圖狀態的方法,在save()和restore()之間可以隨意的更改canvas的狀態而不影響後續的繪製效果。

window.onload=function(){
    var canvas=document.getElementById("canvas");

    canvas.width=400;
    canvas.height=400;

    var context=canvas.getContext("2d");

    context.save();
    context.fillStyle="red";
    context.translate(50,50);
    context.fillRect(0,0,200,200);
    //context.translate(-50,-50);//反向操作
    context.restore();

    context.save()
    context.fillStyle="green";
    context.translate(150,150);
    context.fillRect(0,0,200,200);
    // context.translate(-150,-150);//反向操作
    context.restore();
}   

Note:繪製整體元素,特別是在其中使用圖形變換的時候,都應該先save()一下,最終結束繪製時再restore()一下以保證canvas圖形繪製的正確。

三、應用translate,rotate和scale

1、使用translate和rotate繪製固定大小星星的星空

沒有用scale.

ctx.translate(x,y); ctx.rotate(rot/180*Math.PI);
window.onload=function(){
    var canvas=document.getElementById("canvas");

    canvas.width=800;
    canvas.height=800;

    var context=canvas.getContext("2d");
    
    context.fillStyle="black";
    context.fillRect(0,0,canvas.width,canvas.height);

    for(var i=0;i<200;i++){
        var r=Math.random()*10+10;
        var x=Math.random()*canvas.width;
        var y=Math.random()*canvas.height;
        var a=Math.random()*360;
        drawStar(context,x,y,r,a);
    }
    
}        


//rot順時針旋轉的角度
function drawStar(ctx,x,y,R,rot){
    
    ctx.save();

    ctx.translate(x,y);
    ctx.rotate(rot/180*Math.PI);
    starPath(ctx);
    //繪製在(x,y)大小為R,旋轉rot度的五角星

    ctx.fillStyle="#fb3";
    ctx.strokeStyle="#fd5";
    ctx.lineWidth=3;
    ctx.lineJoin="round";

    ctx.fill();
    ctx.stroke();

    ctx.restore();
}


function starPath(ctx){
    ctx.beginPath();
    //角度轉弧度:除以180*PI
    for(var i=0;i<5;i++){
        ctx.lineTo(Math.cos((18+i*72)/180*Math.PI)*20,
            -Math.sin((18+i*72)/180*Math.PI)*20);
            ctx.lineTo(Math.cos((54+i*72)/180*Math.PI)*0.5*20,
            -Math.sin((54+i*72)/180*Math.PI)*0.5*20);
    }
    ctx.closePath();
}
View Code

效果和上面圖片一樣。

2、scale副作用

不僅放在大小,還會放大坐標,邊框等。

 var canvas=document.getElementById("canvas");

    canvas.width=400;
    canvas.height=400;

    var context=canvas.getContext("2d");

    context.save();
    context.scale(1,1);
    context.strokeRect(10,10,100,100);
    context.restore();

    context.save()
    context.scale(2,2,);
    context.strokeRect(10,10,100,100);
    context.restore();

    context.save()
    context.scale(3,3,);
    context.strokeRect(10,10,100,100);
    context.restore();
}   

3, 應用scale繪製星空

坐標是通過translate變換的,始終是(0,0)所以scale後還是(0,0)。

放棄外邊框的繪製。

window.onload=function(){
    var canvas=document.getElementById("canvas");

    canvas.width=800;
    canvas.height=800;

    var context=canvas.getContext("2d");
    
    context.fillStyle="black";
    context.fillRect(0,0,canvas.width,canvas.height);

    for(var i=0;i<200;i++){
        var r=Math.random()*10+10;
        var x=Math.random()*canvas.width;
        var y=Math.random()*canvas.height;
        var a=Math.random()*360;
        drawStar(context,x,y,r,a);
    }
    
}        


//rot順時針旋轉的角度
function drawStar(ctx,x,y,R,rot){
    
    ctx.save();

    ctx.translate(x,y);
    ctx.rotate(rot/180*Math.PI);
    ctx.scale(R,R);

    starPath(ctx);
    //繪製在(x,y)大小為R,旋轉rot度的五角星

    ctx.fillStyle="#fb3";
    //放棄外邊框的繪製
    // ctx.strokeStyle="#fd5";
    // ctx.lineWidth=3;
    // ctx.lineJoin="round";

    ctx.fill();
    // ctx.stroke();

    ctx.restore();
}


function starPath(ctx){
    ctx.beginPath();
    //角度轉弧度:除以180*PI
    for(var i=0;i<5;i++){
        ctx.lineTo(Math.cos((18+i*72)/180*Math.PI),
            -Math.sin((18+i*72)/180*Math.PI));
            ctx.lineTo(Math.cos((54+i*72)/180*Math.PI)*0.5,
            -Math.sin((54+i*72)/180*Math.PI)*0.5);
    }
    ctx.closePath();
}
View Code

星星沒有外邊框。

四、深入理解圖形變換

圖形變換的實質是對圖形的頂點坐標的再計算。計算過程通過變換矩陣來完成。

二維的變換矩陣是3*3,三維的變換矩陣是4*4。

使用transform(a,b,c,d,e,f)設置變換矩陣每次設置是在之前的基礎上設置的。

可以用setTransform(a,b,c,d,e,f))忽略掉之前所有的變換矩陣。先設置為單位矩陣再變換。

window.onload=function(){
    var canvas=document.getElementById("canvas");

    canvas.width=400;
    canvas.height=400;

    var context=canvas.getContext("2d");

    context.fillStyle="red";
    context.strokeStyle="#058";
    context.lineWidth=5;
    /////////////////////////////
    // a c e
    // b d f 
    // 0 0 1
    /////////////////////////////
    // a,d 水平,垂直縮放
    // b,c 水平,垂直傾斜
    // e,f 水平,垂直位移
    /////////////////////////////
    context.save();
    // context.transform(1,0,0,1,0,0);
    //transform級聯操作
    context.transform(1,0,0,1,50,100);
    context.transform(2,0,0,1.5,0,0);
    context.transform(1,-0.2,-0.2,1,0,0);
    //setTransform()只使用當前變換
    context.setTransform(1,0,0,1,100,100);
    context.fillRect(50,50,100,100);
    context.strokeRect(50,50,100,100);
    context.restore();
} 

這部分內容和css3的動畫的內容本質都是一樣的,都是圖形學的內容。

css3動畫可以參考我之前的博客:

css3中變形與動畫(一)

css3中變形與動畫(二)

css3中變形與動畫(三)

 

本文作者starof,因知識本身在變化,作者也在不斷學習成長,文章內容也不定時更新,為避免誤導讀者,方便追根溯源,請諸位轉載註明出處:http://www.cnblogs.com/starof/p/8626422.html 有問題歡迎與我討論,共同進步。 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本篇雖然介紹的是消息彈窗,但分享的代碼,都是IT連里完整的功能模塊了。認真掃代碼,就能發現用Sagit框架寫代碼是簡潔的不要不要的了。Sagit框架,讓IOS開發更簡單,你值的擁有!!! ...
  • 在design包裡面 有一個 BottomSheetDialogFragment 這個Fragment,他已經幫我們處理好了手勢,所以實現起來很簡單。下麵是代碼: ...
  • 網上有很多限制textField輸入長度方法,但是我覺得都不是很完美,準確來說可以說是不符合實際開發的要求,因此在這裡整理一下textField限制輸入長度的方法.我所採用的並不是監聽方法而是最不同的代理實現方法,為什麼不使用監聽呢???當你看到這篇文章很有可能視是為一件事所苦惱那就是使用監聽限制輸 ...
  • Android有很多特別的xml文件,如常用的selector、style以及shape,熟練使用這些xml可以是我們的項目變得更個性化。 一、子標簽(corners、gradient、padding、size、solid、stroke) 1、padding和size 這兩個可以選擇不用,因為它們的 ...
  • 冒泡排序:時間複雜度為O(n^2) 比較任意兩個相鄰的項,如果第一個比第二個大,則交換它們,元素向上移動至正確的順序,就好像氣泡升至錶面上一樣。 冒泡排序是排序演算法中最簡單的,然而,從運行事件的角度來看,冒泡排序是最差的一個, 首先我們來講解一下思路吧: ...
  • Vue.js在設計上使用MVVM模式。通過MVVM的模式拆分為視圖和數據兩部分,並將其分離。因此,你只需關心數據即可,DOM的事情Vue會幫你自動搞定。 Vue的生命周期鉤子比較常用的有: created 實例創建完成之後釣魚,此階段完成了數據的觀測等,但尚未掛載,$el還不可用。在需要初始化處理一 ...
  • 一、生命後期 官網的圖片說明: Vue的生命周期總結 二、數據綁定 1、數據綁定語法 【文本插值】 【HTML屬性】 【綁定表達式】 【過濾器】 【指令】 2、計算屬性(computed) 3、表單控制 【表單控制項】 script部分 需要註意的是sel2val的賦值是int數組,可是在選擇值得時候 ...
  • 基本概念 佈局模型 1. 流動模型(Flow) 2. 浮動模型(Float) 3. 層模型(Layer) 流動模型 預設的網頁佈局模式,流動佈局模型有兩個比較典型的特征: 第一,塊級元素都會在所處的包含元素內自上而下按順序垂直延伸分佈,且預設狀態下,會計元素的寬度都為包含元素寬度的 100% ,即塊 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...