(第四天)HTML5之Canvas圖形繪制:圖形、moveTo&lineTo&beginPath&closePath六敬、貝塞爾曲線碘赖、漸變、縮放平移旋轉(zhuǎn)及上下文狀態(tài)保存及還原

Canvas圖形繪制 <small>API參考</small>


  • 開(kāi)始創(chuàng)建路徑ctx.beginPath()外构,通過(guò)此方法避免前面的圖形重復(fù)繪制普泡。
  • (以圓形為例)創(chuàng)建圓形路徑ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise)
    <small>
    • x:圓心X坐標(biāo);
    • y:圓心Y坐標(biāo)审编;
    • radius:圓半徑撼班;
    • startAngle:起始弧度,一般為0垒酬;根據(jù)情況而定砰嘁;
    • endAngle:結(jié)束弧度,2 * Math.PI 表示360°勘究;
    • anticlockwise:可選值矮湘;默認(rèn)為false,即順時(shí)針繪制口糕;如果為 true缅阳,逆時(shí)針繪制圓弧。
      </small>
  • 關(guān)閉路徑ctx.closePath():將筆點(diǎn)返回到當(dāng)前子路徑起始點(diǎn)的方法景描;它嘗試從當(dāng)前點(diǎn)到起始點(diǎn)繪制一條直線十办。 如果圖形已經(jīng)是封閉的或者只有一個(gè)點(diǎn),那么此方法不會(huì)做任何操作超棺。

<small>若不需要封閉非整圓橘洞,請(qǐng)勿使用該方法;否則會(huì)在非整圓終點(diǎn)至起始點(diǎn)繪制一條直線说搅。</small>

  • 設(shè)置畫(huà)筆繪制樣式<small>(填充ctx.fillStyle;劃線ctx.strokeStyle虏等,線寬ctx.lineWidth弄唧。)</small>
  • 開(kāi)始繪制圖形<small>(填充ctx.fill(),劃線ctx.stroke())</small>
<!--canvas arc demo-->
    <script>
    "use strict"
    window.onload = function() {
        var canvas = document.getElementById("canvas");
        if (canvas == null) {
            return false;
        }
        // 獲得一個(gè)2d渲染上下文
        var ctx = canvas.getContext("2d");
        for (var i = 1; i < 10; i++) {
            // 創(chuàng)建路徑霍衫;如果不重新創(chuàng)建路徑候引,存在的圖形路徑會(huì)重復(fù)繪制
            ctx.beginPath();
            //創(chuàng)建圖形路徑; 
            ctx.arc(50 * i, 50 * i, 20 * i, 0, 2 * Math.PI, true);
            //閉合路徑;
            ctx.closePath();
            //設(shè)置填充顏色
            ctx.fillStyle = "rgba(255,0,0,0.25)";
            //開(kāi)始填充圖形
            ctx.fill();
            // 劃筆樣式
            ctx.strokeStyle = "#ff0";
            // 劃線渲染
            ctx.stroke();
        }
    }
    </script>
    <canvas id="canvas" width="1000" height="1000"></canvas>
</body>

moveTo & lineTo & beginPath & closePath


  • moveTo(x,y):將畫(huà)筆移動(dòng)到x,y坐標(biāo)(該坐標(biāo)為第一個(gè)子路徑的終點(diǎn))敦跌;
  • lineTo(x,y):直線連接子路徑的終點(diǎn)到x澄干,y坐標(biāo)(只連接逛揩,不繪制);
  • beginPath():通過(guò)清空子路徑列表開(kāi)始一個(gè)新路徑的方法麸俘。 當(dāng)你想創(chuàng)建一個(gè)新的路徑時(shí)辩稽,調(diào)用此方法;
  • closePath():從當(dāng)前點(diǎn)到起始點(diǎn)繪制一條直線从媚。 如果圖形已經(jīng)是封閉的或者只有一個(gè)點(diǎn)逞泄,那么此方法不會(huì)做任何操作。

重點(diǎn):沒(méi)有子路徑時(shí)(即沒(méi)有moveTo或lineTo時(shí))拜效,lineTo作用與moveTo相同

代碼示例:
<body>
<script>
    window.onload = function () {
        var canvas = document.getElementById("canvas");
        if (canvas == null) {
            return false;
        }
        var context = canvas.getContext("2d");
        //創(chuàng)建路徑
        context.beginPath();
        //移動(dòng)畫(huà)筆 1.將畫(huà)筆移動(dòng)到50喷众,50的坐標(biāo)
        context.moveTo(50, 50);
        //畫(huà)第一條線 2.從moveTo的50,50到lineTo的400,50坐標(biāo)劃一條線
        context.lineTo(100, 50);
        //畫(huà)第二條線 3.從上一個(gè)lineTo的400,50到這個(gè)lineTo的50,400坐標(biāo)劃一條線
        context.lineTo(50, 100);
        //從當(dāng)前點(diǎn)(50,400)到起始點(diǎn)(beginPath后moveTo的50,50)劃一條線
        context.closePath();
        //畫(huà)筆顏色
        context.strokeStyle = "#ccc";
        //線的寬度
        context.lineWidth = "5";
        //渲染,即開(kāi)始繪畫(huà)
        context.stroke();
        /*該例子中closePath()相當(dāng)于lineto(50,50);故這條線是50,50到100,100兩點(diǎn)的直線*/
        context.lineTo(100, 100);
        context.stroke();

        /*清空路徑列表紧憾,重新創(chuàng)建一個(gè)新的路徑*/
        context.beginPath();
        /*當(dāng)前不存在子路徑到千,故此方法作用與moveTo(200,200)相同*/
        context.lineTo(200, 200);
        /*將200,200這個(gè)子路徑終點(diǎn)連接到250,250*/
        context.lineTo(250, 250);
        /*將連接的直線繪制出來(lái)*/
        context.stroke();
    }
</script>
<canvas id="canvas" height="300" width="500" style="background-color: #ff0;"></canvas>
</body>
上述代碼示例效果圖

繪制貝塞爾曲線 <small>bezierCurve API參考</small>


  • 參數(shù)
    • cp1x:第一個(gè)控制點(diǎn)的 x 軸坐標(biāo);
    • cp1y:第一個(gè)控制點(diǎn)的 y 軸坐標(biāo)赴穗;
    • cp2x:第二個(gè)控制點(diǎn)的 x 軸坐標(biāo)憔四;
    • cp2y:第二個(gè)控制點(diǎn)的 y 軸坐標(biāo);
    • x:結(jié)束點(diǎn)的 x 軸坐標(biāo)望抽;
    • y:結(jié)束點(diǎn)的 y 軸坐標(biāo)加矛。
  • 重點(diǎn):繪制beziercurve重點(diǎn)在于掌握cp1和cp2兩個(gè)點(diǎn)的控制;曲線的起點(diǎn)為畫(huà)筆的起點(diǎn)煤篙,曲線的終點(diǎn)為參數(shù)x斟览,y的坐標(biāo),曲線的彎曲度由cp1和cp2兩個(gè)點(diǎn)位置的牽引力決定辑奈。
<body>
    <!-- beziercurve demo -->
    <script>
    window.onload = function() {
        var canvas = document.getElementById("canvas");
        if (canvas == null) {
            return false;
        }
        var ctx = canvas.getContext("2d");
        ctx.strokeStyle = "#ddd";
        ctx.lineWidth = 1;

        //畫(huà)橫線苛茂,y軸每隔10個(gè)像素畫(huà)一條橫線
        for (var i = 0.5; i < 500; i++) {
            //創(chuàng)建路徑,如果沒(méi)有此方法鸠窗,循環(huán)會(huì)導(dǎo)致里面的線會(huì)重復(fù)渲染妓羊。可比較"畫(huà)橫線"與"畫(huà)豎線"的區(qū)別稍计。
            //ctx.beginPath();
            ctx.moveTo(0, i * 10);
            ctx.lineTo(500, i * 10);
            //若該渲染方法寫在for循環(huán)之外躁绸,即使沒(méi)有beginPath()方法,也不會(huì)重復(fù)渲染臣嚣。
            ctx.stroke();
        }

        //畫(huà)豎線净刮,x軸每隔10個(gè)像素畫(huà)一條豎線
        for (var i = 0.5; i < 500; i++) {
            ctx.beginPath();
            ctx.moveTo(i * 10, 0);
            ctx.lineTo(i * 10, 500);
            ctx.stroke();
        }
        // beziercurve begin
        ctx.beginPath();
        ctx.moveTo(100, 100);
        ctx.bezierCurveTo(100, 400, 400, 400, 400, 100);
        //如果圖形已經(jīng)是封閉的或者只有一個(gè)點(diǎn),那么此方法不會(huì)做任何操作硅则。否則該方法會(huì)嘗試從當(dāng)前點(diǎn)到起始點(diǎn)繪制一條直線淹父。
        // ctx.closePath();
        ctx.strokeStyle = "#f00";
        ctx.stroke();
        // beziercurve end

        /*下方代碼更好的說(shuō)明bezierCurverTo六個(gè)參數(shù)的作用*/

        //第一個(gè)控制點(diǎn),曲線的牽引方向點(diǎn)怎虫;bezierCurveTo第一個(gè)x暑认,y
        drawArc(ctx, 100, 400, 5, 0, 2 * Math.PI, "#f00", "控制點(diǎn)1");
        //第二個(gè)控制點(diǎn)困介,曲線的牽引方向點(diǎn);bezierCurveTo第二個(gè)x蘸际,y
        drawArc(ctx, 400, 400, 5, 0, 2 * Math.PI, "#f00", "控制點(diǎn)2");
        //開(kāi)始點(diǎn)座哩,moveTo的點(diǎn)
        drawArc(ctx, 100, 100, 5, 0, 2 * Math.PI, "#00f", "起始點(diǎn)");
        //結(jié)束點(diǎn),相當(dāng)于lineTo的點(diǎn)捡鱼;bezierCurveTo第三個(gè)x八回,y
        drawArc(ctx, 400, 100, 5, 0, 2 * Math.PI, "#00f", "結(jié)束點(diǎn)");

    }

    // 繪制一個(gè)圓點(diǎn)及旁注
    function drawArc(ctx, x, y, r, startAngle, endAngle, color, txt) {
        ctx.beginPath();
        ctx.arc(x, y, r, startAngle, endAngle);
        ctx.fillStyle = color;
        ctx.fill();
        ctx.strokeStyle = color;
        // 繪制字符
        ctx.strokeText(txt, x + r + 5, y);
    }
    </script>
    <canvas id="canvas" width="500" height="500"></canvas>
</body>

效果圖如下:


貝塞爾曲線示例圖

Canvas的漸變


漸變過(guò)程中的顏色均使用漸變對(duì)象.addColorStop(offset,color)方法設(shè)置,多少種顏色漸變就添加多少次此方法驾诈。

  • 線性漸變LinearGradient

該漸變對(duì)象只定義漸變的起始與終點(diǎn)位置及漸變的角度缠诅,在該兩點(diǎn)位置之間實(shí)現(xiàn)對(duì)應(yīng)角度的漸變。
漸變的有效范圍:在canvas范圍內(nèi)根據(jù)兩點(diǎn)位置及角度決定了漸變范圍乍迄。<small>關(guān)于漸變色的角度:漸變色位于兩點(diǎn)構(gòu)成直線的垂直線范圍內(nèi)</small>

1. 創(chuàng)建LinearGradient對(duì)象
  `var lgradient = ctx.createLinearGradient(xstart,ystart,xend,yend)`
2. 添加漸變顏色`lgradient.addColorStop(offset,color)`<small>多少種顏色漸變就添加多少次此方法</small>
3. 將圖形繪制(`ctx.fillRect等`)在漸變對(duì)象有效的canvas范圍內(nèi)(非漸變對(duì)象的參數(shù)范圍)
<!--LinearGradient & 角度弧度轉(zhuǎn)化 & 根據(jù)角度管引、圓心、半徑獲取旋轉(zhuǎn)后的目標(biāo)坐標(biāo) DEMO-->
<body>
    <script>
    window.onload = function() {
        var canvas = document.getElementById("canvas");
        if (canvas == null) {
            return false;
        }
        var ctx = canvas.getContext("2d");
        // 創(chuàng)建一個(gè)線性漸變對(duì)象闯两,并指定起始x,y坐標(biāo)及結(jié)束x,y坐標(biāo)
        var gradient = ctx.createLinearGradient(0, 0, 100, 50);

        // 漸變色添加 begin褥伴;真正的漸變?yōu)間radient范圍內(nèi)的50%到80%之間,從白色漸變到綠色
        // offset=0.8漾狼,表示green的正色在gradient范圍的80%處(從左往右)重慢;該offset后的范圍均為green正色
        gradient.addColorStop(0.8, "green");
        // offset=0.5,表示white的正色在gradient范圍的50%處(從左往右)逊躁;該offset前的范圍均為white正色
        gradient.addColorStop(0.5, "white");
        // 漸變色添加 end

        ctx.fillStyle = gradient;
        // 此處300已超出LinearGradient對(duì)象的范圍及角度產(chǎn)生的漸變范圍似踱,故超出有效漸變范圍會(huì)根據(jù)addColorStop設(shè)置的偏移和顏色決定,此處顯示green色稽煤。
        ctx.fillRect(10, 10, 300, 100);

        // 漸變的范圍核芽,超出該范圍,都使用接近的正色
        ctx.strokeStyle = "#E3C41B";
        ctx.strokeRect(0, 0, 100, 50);

        // 繪制漸變的方向
        ctx.beginPath();
        ctx.moveTo(0, 0);
        ctx.lineTo(100, 50);
        ctx.stroke();
        // 求斜線的長(zhǎng)度酵熙,得出半徑
        var r = Math.sqrt(Math.pow(100, 2) + Math.pow(50, 2)) / 2;
        ctx.beginPath();
        ctx.moveTo(50, 25);
        var degree = getDegree(0, 0, 100, 50);
        // 注:*****所有的角度都根據(jù)當(dāng)時(shí)坐標(biāo)點(diǎn)上的坐標(biāo)軸計(jì)算,正數(shù)則按順時(shí)針計(jì)算*****
        // 1角度=π/180弧度
        //dx 應(yīng)該是順時(shí)針偏移斜線與x坐標(biāo)軸的順時(shí)針夾角+90度角算出的x軸坐標(biāo)
        var dx = 50 + Math.cos(degree * (Math.PI / 180) + Math.PI / 2) * r;
        var dy = 25 + Math.sin(degree * (Math.PI / 180) + Math.PI / 2) * r;
        ctx.lineTo(dx, dy);
        ctx.strokeStyle = "#f00";
        ctx.stroke();

        // 弧度的輔助線
        ctx.beginPath();
        ctx.moveTo(75, 25);
        var cpx1 = cpx2 = 100 - 35;
        var cpy1 = cpy2 = dy - 30;
        ctx.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, dx + (50 - dx) / 2, (dy - 25) / 2 + 25);
        ctx.stroke();
        ctx.strokeStyle = "#000";
        ctx.strokeText("dx,dy參數(shù)中的弧度", cpx1, cpy1);

        // 中心點(diǎn)坐標(biāo)軸
        ctx.beginPath();
        ctx.moveTo(0, 25);
        ctx.lineTo(100, 25);
        ctx.strokeText("x軸", 100, 25);
        ctx.moveTo(100 * 0.5, 0);
        ctx.lineTo(100 * 0.5, 100);
        ctx.strokeText("y軸 offset:0.5", 100 * 0.5, 100);
        // 0.8offset的垂直線
        ctx.moveTo(100 * 0.8, 0);
        ctx.lineTo(100 * 0.8, 80);
        ctx.strokeText("offset:0.8", 100 * 0.8, 80);
        ctx.strokeStyle = "#00a";
        ctx.stroke();

        // 注釋
        ctx.strokeText("黃色斜線位置與方向:", 10, 150);
        ctx.strokeText("offset就是顏色正色在該斜線x軸上的偏移量;", 10, 170);
        ctx.strokeText("圖中所示offset與黃色斜線相交點(diǎn):0.8與0.5兩個(gè)點(diǎn);", 10, 190);
        ctx.strokeText("兩色之間做漸變轧简,根據(jù)斜線方向及整個(gè)屏幕范圍漸變;", 10, 210);
        ctx.strokeText("將圖形的style設(shè)置為漸變對(duì)象,并在漸變屏幕范圍", 10, 230);
        ctx.strokeText("內(nèi)繪制即可得到漸變效果", 10, 250);
    }

    //獲取兩點(diǎn)之間的直線與起始點(diǎn)坐標(biāo)x軸順時(shí)針的角度
    function getDegree(sx, sy, dx, dy) {
        // 直角的邊長(zhǎng)
        var x = Math.abs(sx - dx);
        var y = Math.abs(sy - dy);
        // 根據(jù)勾股定理 斜邊的平方 = 兩直角邊的平方之和
        var z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
        // cos
        var cos = x / z;
        // 獲取弧度
        var angle = Math.acos(cos);
        // 轉(zhuǎn)化為角度匾二;1弧度 = 180/π角度
        var degree = 180 / Math.PI * angle;
        console.log(degree);
        return degree;
    }
    </script>
    <canvas id="canvas" height="500" width="500"></canvas>

以上代碼效果圖如下所示:

LinearGradient示例圖(二色漸變)
  • 徑向漸變RadialGradient

該漸變對(duì)象實(shí)現(xiàn)射線式徑向漸變哮独,其有效范圍為:通過(guò)起始圓形兩邊射線到結(jié)束圓形兩邊并相匯處

1. 創(chuàng)建RadialGradient對(duì)象
  `var lgradient = ctx.createRadialGradient(xstart,ystart,rstart,xend,yend,rend)`
2. 添加漸變顏色`lgradient.addColorStop(offset,color)`<small>多少種顏色漸變就添加多少次此方法</small>
3. 將圖形繪制(`ctx.fillRect等`)在漸變對(duì)象有效范圍內(nèi)(非漸變對(duì)象的參數(shù)范圍)
<!-- RadialGradient Demo-->
<body>
    <script>
    window.onload = function() {
        var canvas = document.getElementById("canvas");
        if (canvas == null) {
            return false;
        }
        var ctx = canvas.getContext("2d");
        // 創(chuàng)建射線(徑向)漸變對(duì)象,六個(gè)參數(shù)分別是:起始圓形坐標(biāo)x察藐,y借嗽,半徑,結(jié)束圓形坐標(biāo)x转培,y,半徑浆竭;
        // 通過(guò)起始圓形兩邊射線到結(jié)束圓形兩邊并相匯是結(jié)束
        var radiaGradient = ctx.createRadialGradient(50, 50, 50, 200, 200, 400);
        // 設(shè)置三個(gè)漸變顏色
        radiaGradient.addColorStop(0.3, "#ff0");
        radiaGradient.addColorStop(0.8, "rgb(255,140,0");
        radiaGradient.addColorStop(1, "rgb(255,0,0");
        // 設(shè)置填充顏色為漸變對(duì)象
        ctx.fillStyle = radiaGradient;
        // 繪制一個(gè)矩形
        ctx.fillRect(0, 0, 500, 500);

    }
    </script>
    <canvas id="canvas" height="500" width="500"></canvas>

以上代碼效果圖如下:

徑向漸變示例圖(三色漸變)

Canvas的縮放浸须、平移惨寿、旋轉(zhuǎn)與ctx的狀態(tài)保存及還原


  • 縮放:scale(x,y):x,y為縮放因子删窒,會(huì)影響縮放對(duì)象的所有參數(shù)(如寬高裂垦、坐標(biāo)、偏移量肌索,但不影響角度)蕉拢;
  • 平移:translate(x,y):x,y為坐標(biāo)軸的偏移量诚亚,即x表示目標(biāo)x軸與當(dāng)前x軸的距離晕换,y表示目標(biāo)y軸與當(dāng)前y軸的距離;
  • 旋轉(zhuǎn):rotate(angle):旋轉(zhuǎn)的弧度站宗;angle = degree * (Math.PI / 180) 闸准;degree為角度。旋轉(zhuǎn)后梢灭,ctx的原坐標(biāo)軸也會(huì)隨著旋轉(zhuǎn)夷家,旋轉(zhuǎn)后的ctx狀態(tài)會(huì)導(dǎo)致后期繪制的圖形其坐標(biāo)軸也是基于旋轉(zhuǎn)后的坐標(biāo)軸繪制的;

注意三個(gè)方法前后順序的變化所產(chǎn)生的影響敏释。
1.translate库快、rotate、scale作用的是整個(gè)ctx
2.rotate的旋轉(zhuǎn)的是ctx環(huán)境钥顽,是根據(jù)ctx的當(dāng)前坐標(biāo)軸x軸進(jìn)行的义屏,正弧度表示順時(shí)針,反之逆時(shí)針耳鸯;旋轉(zhuǎn)后起始坐標(biāo)軸更新到旋轉(zhuǎn)后的坐標(biāo)軸狀態(tài)
3.translate的參數(shù)為距x軸的距離湿蛔,距y軸的距離;即起始點(diǎn)x,y的偏移量;同樣是根據(jù)ctx的當(dāng)前狀態(tài)坐標(biāo)軸進(jìn)行
translate、rotate县爬、scale會(huì)被ctx記錄狀態(tài)阳啥,值會(huì)被疊加

  • ctx.save():將當(dāng)前上下文狀態(tài)保存,以便后期使用财喳;以便某狀態(tài)被覆蓋的時(shí)候使用察迟;
  • ctx.restore():還原至最近save的上下文狀態(tài)。
<!--scale translate rotate & save restore demo-->
<body>
    <script>
    window.onload = function() {
        var canvas = document.getElementById("canvas");
        if (canvas == null) {
            return false;
        }
        var ctx = canvas.getContext("2d");

        ctx.strokeStyle = "#ddd";
        ctx.lineWidth = 1;

        //畫(huà)橫線耳高,y軸每隔10個(gè)像素畫(huà)一條橫線
        for (var i = 0.5; i < 500; i++) {
            //創(chuàng)建路徑扎瓶,如果沒(méi)有此方法,循環(huán)會(huì)導(dǎo)致里面的線會(huì)重復(fù)渲染泌枪「藕桑可比較"畫(huà)橫線"與"畫(huà)豎線"的區(qū)別。
            //ctx.beginPath();
            ctx.moveTo(0, i * 10);
            ctx.lineTo(500, i * 10);
            //若該渲染方法寫在for循環(huán)之外碌燕,即使沒(méi)有beginPath()方法误证,也不會(huì)重復(fù)渲染继薛。
            ctx.stroke();
        }

        //畫(huà)豎線,x軸每隔10個(gè)像素畫(huà)一條豎線
        for (var i = 0.5; i < 500; i++) {
            ctx.beginPath();
            ctx.moveTo(i * 10, 0);
            ctx.lineTo(i * 10, 500);
            ctx.stroke();
        }

        ctx.fillStyle = "#ff0";
        ctx.fillRect(50, 50, 50, 100);
        // 保存當(dāng)前狀態(tài)
        ctx.save();
        ctx.translate(50, 50);
        ctx.fillStyle = "#0f0";
        ctx.fillRect(50, 50, 50, 100);
        // 使用restore后愈捅,最近的save狀態(tài)就會(huì)被彈出遏考;
        ctx.restore();
        // 重新保存還原后得到的第一個(gè)save狀態(tài)
        ctx.save();
        for (var i = 0; i < 3; i++) {
            // ctx.scale(0.9, 0.9);
            // x,y分別正向偏移各50像素
            ctx.translate(50, 50);
            ctx.fillStyle = "rgba(255,0,0,0.25)";
            // 以偏移后的50,50坐標(biāo)點(diǎn)做為起始的0,0坐標(biāo)點(diǎn)蓝谨,下方的50,50坐標(biāo)點(diǎn)以原始0,0計(jì)算則為100,100的坐標(biāo).
            ctx.fillRect(50, 50, 100, 20);
        }
        // 恢復(fù)到save保存的狀態(tài)
        ctx.restore();
        ctx.translate(300, 300);
        // ctx會(huì)記錄繪制的狀態(tài)灌具,循環(huán)會(huì)導(dǎo)致在記錄的狀態(tài)下不斷的scale,不斷的translate譬巫。
        for (var i = 0; i < 50; i++) {
            // 先縮放咖楣,后平移,會(huì)導(dǎo)致平移的50,50偏移量隨縮放因子改變缕题,及x,y實(shí)際偏移量分別為45,45截歉。
            // 即使先平移,后縮放烟零,因循環(huán)也會(huì)導(dǎo)致平移的參數(shù)隨縮放因子發(fā)生變化瘪松。
            // 說(shuō)明ctx的狀態(tài)被實(shí)時(shí)覆蓋保存,除非通過(guò)save()存檔副本
            ctx.scale(0.9, 0.9);
            ctx.translate(10, 10);
            // 順時(shí)針30度角旋轉(zhuǎn)
            ctx.rotate(30 * Math.PI / 180);
            ctx.fillStyle = "rgba(0,0,255,0.25)";
            // scale的縮放因子為0.9,所以實(shí)際坐標(biāo)為偏移后的x坐標(biāo)+45,偏移后的y坐標(biāo)+0锨阿;寬高為90,18宵睦。
            ctx.fillRect(50, 0, 100, 20);
        }
    }
    </script>
    <canvas id="canvas" width="500" height="500"></canvas>
</body>

以上代碼效果圖如下:

縮放、平移墅诡、旋轉(zhuǎn)及狀態(tài)保存還原的示例
/* translate rotate 重要知識(shí)點(diǎn) */
/* 重點(diǎn):
 * 1.translate壳嚎、rotate、scale作用的是整個(gè)ctx
 * 2.rotate的旋轉(zhuǎn)的是ctx環(huán)境末早,是根據(jù)ctx的當(dāng)前坐標(biāo)軸x軸進(jìn)行的烟馅,正弧度表示順時(shí)針,反之逆時(shí)針然磷;旋轉(zhuǎn)后起始坐標(biāo)軸更新到旋轉(zhuǎn)后的坐標(biāo)軸狀態(tài)
 * 3.translate的參數(shù)為距x軸的距離郑趁,距y軸的距離;即起始點(diǎn)x,y的偏移量;同樣是根據(jù)ctx的當(dāng)前狀態(tài)坐標(biāo)軸進(jìn)行
 * translate、rotate姿搜、scale會(huì)被ctx記錄狀態(tài)寡润,值會(huì)被疊加
 * */
var secCanvas = document.getElementById("secCanvas");
secCanvas.style.backgroundColor = "#ccc";
ctx = secCanvas.getContext("2d");
for(var i = 0; i < 2; i++) {
    ctx.beginPath();
    //標(biāo)識(shí)ctx的起始點(diǎn) beigin (藍(lán)色原點(diǎn))
    ctx.arc(0, 0, 5, 0, 2 * Math.PI, false);
    ctx.fill();
    //標(biāo)識(shí)ctx的起始點(diǎn) end
    ctx.fillStyle = "royalblue";
    //scale不影響角度,只影響寬高舅柜、坐標(biāo)梭纹、偏移量
    //ctx.scale(0.9,0.9);
    var angle = 45 * (Math.PI / 180);
    //基于起點(diǎn)坐標(biāo)軸x軸順時(shí)針旋轉(zhuǎn)45度角
    //rotate后,ctx的整個(gè)坐標(biāo)軸就旋轉(zhuǎn)了45度角(圖中的紅線為旋轉(zhuǎn)后的x軸)
    ctx.rotate(angle);/*因?yàn)閏tx會(huì)更新?tīng)顟B(tài)致份,所以會(huì)作用到第二次循環(huán)*/
    ctx.save(); /*保存旋轉(zhuǎn)后的狀態(tài),以便下方劃坐標(biāo)軸用*/

    //劃旋轉(zhuǎn)angle弧度后的線條 beigin
    ctx.beginPath();
    ctx.moveTo(0, 0);
    //ctx已經(jīng)旋轉(zhuǎn)变抽,這里獲取旋轉(zhuǎn)角度后對(duì)應(yīng)100半徑的坐標(biāo)點(diǎn),角度只需0即可
    var dx = Math.cos(0) * 500;
    var dy = Math.sin(0) * 500;
    ctx.lineTo(dx, dy);
    ctx.strokeStyle = "#FF0000";
    ctx.stroke();
    ctx.font = "14px Consolas";
    ctx.strokeStyle = "#228B22";
    ctx.strokeText("第"+(i+1)+"個(gè)循環(huán)旋轉(zhuǎn)45度后的x坐標(biāo)軸", dx, dy)
        //劃旋轉(zhuǎn)angle弧度后的線條 end

    //標(biāo)識(shí)未translate(50,0)前的rect起始點(diǎn)(紅色點(diǎn))
    ctx.strokeText("第"+(i+1)+"個(gè)循環(huán)旋轉(zhuǎn)45度后的100,0坐標(biāo)點(diǎn)", 100, 0)
    ctx.beginPath();
    ctx.arc(100, 0, 5, 0, 2 * Math.PI, false);
    ctx.fillStyle = "#FF0000";
    ctx.fill();
    //基于起點(diǎn)坐標(biāo)軸x,y軸的偏移距離
    ctx.translate(50, 0);
    ctx.fillRect(100, 0, 100, 50);

    //標(biāo)識(shí)translate(50,0)后的rect起始點(diǎn)(黃色點(diǎn))
    ctx.strokeText("第"+(i+1)+"個(gè)循環(huán)旋轉(zhuǎn)45度且偏移50,0后的100,0坐標(biāo)點(diǎn)", 100, 20)
    ctx.beginPath();
    ctx.arc(100, 0, 5, 0, 2 * Math.PI, false);
    ctx.fillStyle = "#ff0";
    ctx.fill();

    //旋轉(zhuǎn)45度后的坐標(biāo)軸
    ctx.restore(); /*還原到未translate前*/
    ctx.beginPath();
    //劃第一個(gè)100,0坐標(biāo)點(diǎn)的y軸
    //移動(dòng)到第一個(gè)100,0坐標(biāo)點(diǎn)(紅色)
    ctx.moveTo(100, -100);
    ctx.lineTo(100, 100);
    ctx.stroke();
    ctx.font = "14px Arial";
    ctx.strokeText("第"+(i+1)+"個(gè)循環(huán)旋轉(zhuǎn)45度后的y坐標(biāo)軸", 110, -100);

    //劃第二個(gè)100,0坐標(biāo)點(diǎn)的y軸
    ctx.beginPath();
    //基于第一個(gè)100,0坐標(biāo)點(diǎn)绍载,x軸方向偏移50
    ctx.translate(50, 0)/*將作用于第二次循環(huán)的ctx起始點(diǎn)*/
        //實(shí)際moveTo(150,-100)
    ctx.moveTo(100, -100);
    ctx.lineTo(100, 100);
    ctx.stroke();
}
循環(huán)2次的效果圖
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末太伊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子逛钻,更是在濱河造成了極大的恐慌,老刑警劉巖锰提,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曙痘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡立肘,警方通過(guò)查閱死者的電腦和手機(jī)边坤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谅年,“玉大人茧痒,你說(shuō)我怎么就攤上這事∪邗澹” “怎么了旺订?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)超燃。 經(jīng)常有香客問(wèn)我区拳,道長(zhǎng),這世上最難降的妖魔是什么意乓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任樱调,我火速辦了婚禮,結(jié)果婚禮上届良,老公的妹妹穿的比我還像新娘笆凌。我一直安慰自己,他們只是感情好士葫,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布乞而。 她就那樣靜靜地躺著,像睡著了一般为障。 火紅的嫁衣襯著肌膚如雪晦闰。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天鳍怨,我揣著相機(jī)與錄音呻右,去河邊找鬼。 笑死鞋喇,一個(gè)胖子當(dāng)著我的面吹牛声滥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼落塑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼纽疟!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起憾赁,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤污朽,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后龙考,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蟆肆,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年晦款,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了炎功。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缓溅,死狀恐怖蛇损,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情坛怪,我是刑警寧澤淤齐,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站酝陈,受9級(jí)特大地震影響床玻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜沉帮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一锈死、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧穆壕,春花似錦待牵、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至川背,卻和暖如春贰拿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背熄云。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工膨更, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缴允。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓荚守,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子矗漾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 一:canvas簡(jiǎn)介 1.1什么是canvas锈候? ①:canvas是HTML5提供的一種新標(biāo)簽 ②:HTML5 ...
    GreenHand1閱讀 4,667評(píng)論 2 32
  • 啥是canvas泵琳? HTML5 標(biāo)簽用于繪制圖像(通過(guò)腳本,通常是 JavaScript)誊役。不過(guò)虑稼, 元素本身...
    kiaizi閱讀 760評(píng)論 0 4
  • 一、canvas簡(jiǎn)介 1.1 什么是canvas势木?(了解) 是HTML5提供的一種新標(biāo)簽 Canvas是一個(gè)矩形區(qū)...
    Looog閱讀 3,938評(píng)論 3 40
  • 前言:對(duì)《iOS開(kāi)發(fā)系列--打造自己的“美圖秀秀”》文章做了整理如果對(duì)于CGPaht 或CGContextPath...
    LiYaoPeng閱讀 8,371評(píng)論 4 41
  • 心是一塊田及皂,快樂(lè)自己種甫男。 人活著不是靠身體,而是靠心验烧。有時(shí)候板驳,換個(gè)心情,你會(huì)快樂(lè)些碍拆。 心是一塊田若治,靠自己去播種,種...
    醉后揮毫筆有神閱讀 387評(píng)論 0 0