GVKun编程网logo

canvas图形变换(canvas图片变形)

8

对于想了解canvas图形变换的读者,本文将提供新的信息,我们将详细介绍canvas图片变形,并且为您提供关于2d图形变换原理,矩阵换算、9、canvas图形组合方式、android图形变换效果、Ca

对于想了解canvas图形变换的读者,本文将提供新的信息,我们将详细介绍canvas图片变形,并且为您提供关于2d 图形变换原理,矩阵换算、9、canvas图形组合方式、android 图形变换效果、Canvas---Canvas图像处理、图片查看器、图像缩放功能的实现的有价值信息。

本文目录一览:

canvas图形变换(canvas图片变形)

canvas图形变换(canvas图片变形)

平移

translate(x,y) 注意平移的是坐标原点,而不是线条本身

<!DOCTYPE html>
<html lang="en"head>
    Meta charset="UTF-8"title>canvas</style>
        .canvas{border:1px solid pink;}
    bodycanvas class="canvas" id width="600" height="400">您的浏览器不支持canvascanvas>

    script>
        var canvas=document.getElementById("canvas);
         ctxcanvas.getContext(2d);//上下文,绘图环境

        ctx.translate(0,100);
        ctx.moveto();
        ctx.lineto();
        ctx.stroke();
    
    html>

 

 


        ctx.moveto();
        ctx.translate(>

 

 

);        
        ctx.lineto(>

 

 

);
        ctx.stroke();

        ctx.translate();
        ctx.beginPath();
        ctx.moveto();
        
        ctx.stroke();
    
    >

 

 beginPath() 会清空之前的路径,但是不会清空被平移的坐标原点

 

旋转 rotate(reg)


        ctx.strokeStyle''orange;
        ctx.linewidth10;

        ctx.moveto();        
        ctx.stroke();
    
        ctx.rotate(Math.PI/4);
        ctx.beginPath();
        ctx.moveto();        
        ctx.stroke();
    
    >

 

 同理,旋转的是坐标原点,而不是线条

 

缩放 scale(x,y)

;

        ctx.translate(300200);        
        ctx.stroke();

        ctx.scale(15);
        ctx.fillRect(-);
    
    >

 

 

ctx.save() 保存当前环境

ctx.restore() 复原上一次保存的环境(包括线条粗细和颜色设置也会受到影响)

;
        ctx.save();保存了最初还没变换的原点(描边颜色为橙色)

        ctx.linewidth);

        ctx.restore();坐标原点恢复到初始值
500600400);        
        ctx.stroke();


    
    >

 

 这两个建议成对出现

 

时钟表盘的原理演示:

上下文,绘图环境

        画圆
        ctx.translate();
        ctx.arc(502*Math.PI,1)">true);
        ctx.stroke();

        画刻度    
        for( i;i<12++){
            ctx.rotate(Math.PI6);
            ctx.moveto(40);
            ctx.lineto();
        }        
        ctx.stroke();

    
    >

 

总结

以上是小编为你收集整理的 canvas图形变换全部内容。

如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。

2d 图形变换原理,矩阵换算

2d 图形变换原理,矩阵换算

2d 图片变形,由鼠标拖动图片的四个角来达到图片变形效果,效果图:

首先需要了解 canvas 的 transform,也就是 css 的 transform 中的 matrix。(先看看 https://my.oschina.net/u/3914215/blog/5361217 来了解下矩阵与图形变化的原理)

canvas.transform (a,b,c,d,e,f) 和 transform:matrix(a,b,c,d,e,f)

向量 p [x,y] 经过矩阵 A 的变换得到向量 p1 [x1,y1]。(这里的向量是指原点,到对应坐标的有向线段,直接理解为点在坐标中的位置吧,经过变换到新的点)。

至少由三个点组成一个面,三个点各自位置发生变换后得到新的一个面。

点 p1,p2,p3 经过矩阵 A 变换得到 d1,d2,d3。当我们已知这 6 个点的时候来求得矩阵 A,这样就可以达到由我们自己拖动图片某个角来使得图片发生变形。(我们也可以发散下思维,至少由 4 个点来组成体,是不是可以由变换前的 4 个点和变换后的 4 个点来得到 3D 变换矩阵呢?)

我们通过这 6 个点来得到变换矩阵然后交于 css 或者 js 来处理,但是想得到这个矩阵就需要了解线性代数的不少东西。

1、矩阵:mxn 矩阵,n 阶矩阵(n 阶方阵)

2、矩阵的加减法:

A+B=B+A

A+B+C=A+(B+C)

3、矩阵乘法:

kA=Ak ,k 为常数

(k+j) A = kA+jA , k 和 j 为常数

k (A+B) = kA+kB , k 为常数

结合律:ABC = A (BC), k (AB)=(kA) B=A (kB)

分配率:A (B+C) = AB + AC;(B+C) A = BA +CA

注意:AB=AC 不代表 B=C;AB!=BA (除非两者互逆)

4、行列式:行列式如何计算

5、方阵的行列式:方阵 A 的行列式记作 | A|

6、余子式:在 n 阶行列式 | A | 中,把 aij 所在第 i 行第 j 列划去后余下的 n-1 阶行列式叫作 aij 的余子式记作 Mji。

7、代数余子式:(-1)i+jMij 为代数余子式记作 Aij(i+j 是上标,这里打不来....),-1 的 i+j 次方 * Mij

8、伴随矩阵:A 的伴随矩阵记作 A*,伴随举证是行列式 | A | 的各个元素的代数余子式 Aij 所构成的矩阵

9、逆矩阵:对于 n 阶矩阵 A 如果存在 n 阶矩阵 B,使 AB=BA=E,A 的逆矩阵 A-1(A 的 - 1 次方,打不来上标直接叫 A 逆吧)。

    如何求得 A 逆?需要得到矩阵 A 的伴随矩阵 A * 和矩阵 A 的行列式 | A|。A 逆 = A*/|A|。

10、因为 AB=C,A 是变换矩阵,B 是变换前的 3 个点,C 是变换后的三个点,已知变换前后的三个点,就可以求抽虎变换矩阵,然后就可以让其他点按照这种变换矩阵去变化。

了解以上十点就可以得到变换矩阵 A 了。然后我们用 JS 代码来求得 A。

首先用 JS 代码来计算行列式的序

//得到n个数的阶乘结果
 function getJiecheng(n) {
     let permutationCombinationNum = 1;
     for (let i=0 ; i < n ; i++){
         permutationCombinationNum = permutationCombinationNum * (i + 1);
     }
     return permutationCombinationNum;
 }
 //删除数组中的某一个元素并返回新的数组
 function delElemForArr(arr,idx) {
     let newArr = [];
     for(let i=0;i<arr.length;i++){
         if(i == idx)
             continue;
         newArr.push(arr[i]);
     }
     return newArr;
 }
 //是否是数字
 function isRealNum(val){
     // isNaN()函数 把空串 空格 以及NUll 按照0来处理 所以先去除,

     if(val === "" || val ==null){
         return false;
     }
     if(!isNaN(val)){
         //对于空数组和只有一个数值成员的数组或全是数字组成的字符串,isNaN返回false,例如:''123''[][2][''123''],isNaN返回false,
         //所以如果不需要val包含这些特殊情况,则这个判断改写为if(!isNaN(val) && typeof val === ''number'' )
         return true;
     }

     else{
         return false;
     }
 }
 //先定义行列式
 class Determinant{
     constructor(arr) {
         this.arr = arr;//来保存行列式的各个元素
         this.isDeterminat = true;
         this.permutationCombination = [];//保留列排列組合后的數組
         this.permutationCombinationNum = 0;
         this.xu = 0;
         this.verDeterminat();
     }

     //验证行列式是否合法,数组是二维数组,二位数组的行数=列数,每个元素都需要是数字
     verDeterminat(){
         if(Array.isArray(this.arr)){
             let arrLength = this.arr.length;
             firstFor:for (let ind in this.arr){
                 if(Array.isArray(this.arr[ind]) && this.arr[ind].length == arrLength){
                     for (let ind_ in this.arr[ind]){
                         if(isRealNum(this.arr[ind][ind_])){

                         }else{
                             this.isDeterminat = false;
                             break firstFor;
                         }
                     }
                 }else{
                     this.isDeterminat = false;
                     break firstFor;
                 }
             }
         }else{
             this.isDeterminat = false;
         }
     }
     //计算行列式的序,需要先了解逆序,得到逆序的奇偶性
     //得到逆序数
     getReverseOrder(arr){
         let reverseOrder = 0;
         if(Array.isArray(arr))for (let i=0;i<arr.length;i++){
             for (let j=i+1;j<arr.length;j++){
                 if(arr[i] > arr[j]){//前面的大于后面的
                     reverseOrder ++;
                 }
             }
         }
         return reverseOrder;
     }
     //我们以行为主,从第一行到第n行取出不重复的列的数值相当于对列进行排列组合,符号为-1x次方,x=列的逆序数
     //计算行列式的序,得到逆序数后就要对列进行排列组合,行采用1-n来排列,列就得排列组合n!
     //获得排列组合后的列的数组
     getPermutationCombination(){
         let n = this.arr.length;
         let arr = [];
         let permutationCombinationNum = getJiecheng(n);
         for (let i=0 ; i < n ; i++){
             arr.push(i);
         }
         for(let i=0;i < permutationCombinationNum; i++){
             this.permutationCombination.push([]);
         }
         this.getPC(arr);
     }
     getPC(arr){
         for (let i=0 ; i < arr.length ; i++){
             for (let j=0;j < getJiecheng(arr.length-1);j++){
                 this.permutationCombination[this.permutationCombinationNum+j].push(arr[i]);
             }
             this.getPC(delElemForArr(arr,i))
             if(arr.length == 1){
                 this.permutationCombinationNum ++;
             }
         }
     }
     //计算得到行列式的序
     calculate(){
         if(!this.isDeterminat){
             alert(''行列式不合法'');
             return false;
         }
         this.getPermutationCombination();//得到所有的排列组合
         for (let i in this.permutationCombination){
             let res = 1;
             for (let j in this.permutationCombination[i]){
                 res = res * this.arr[j][this.permutationCombination[i][j]];
             }
             if(this.getReverseOrder(this.permutationCombination[i]) % 2 == 0){//逆序数是偶数
                 this.xu = this.xu + res;
             }else{
                 this.xu = this.xu - res;
             }
         }
     }
     //得到这个行列式的代数余子式
     getAij(i,j){
         let newArr = [];//余子式
         for (let k=0;k<this.arr.length-1;k++){
             newArr.push([]);
         }
         let k = 0;
         for (let n in this.arr){
             if(n != i){
                 for (let m in this.arr[n]){
                     if(m != j){
                         newArr[k].push(this.arr[n][m])
                     }
                 }
                 k++;
             }
         }
         console.log(newArr);//输出余子式
         let deter = new Determinant(newArr);//创建行列式对象
         deter.calculate();//计算行列式的序,Mij
         if((i+j) % 2 == 0){//偶数,返回代数余子式Aij
             return deter.xu;
         }else{
             return -deter.xu;
         }

     }

 }
 // var deter = new Determinant([
 //     [0,1,3,4],
 //     [1,2,3,5],
 //     [1,2,3,6],
 //     [1,2,3,7]
 // ]);
 var deter = new Determinant([
     [2,1,3],
     [1,1,3],
     [2,2,3],
 ]);
deter.calculate();
console.log(deter.xu);
var Aij = deter.getAij(1,2);//A12 第一行第二列的代数余子式
console.log(Aij)

行列式相关的计算已经 OK,接下来准备矩阵相关的计算。矩阵主要还是相乘和求逆矩阵。

//数组行列进行兑换
//|1 1 1|       |1 2 3|
//|2 2 2| =>    |1 2 3|
//|3 3 3|       |1 2 3|
function rotateArr(arr) {
   //arr.map(row=>row[0]);//循环arr返回每一行的第一位数组成的新数组
    let newArray = arr[0].map((col, i) => arr.map(row => row[i]));
    return newArray;
}
class Matrix{
    constructor(arr){
        this.arr = arr;//来保存矩阵中的各个元素
        this.isMatrixnm = true;
        this.isMatrixnn = true;
        this.n = this.arr.length;//
        this.m = this.arr[0].length;//
        this.verMatrixnm();
        this.verMatrixnn();
    }
    //验证举证是否合法,数组是二维数组,每一行的列数必须相等,方阵的话列数等于行数
    verMatrixnm(){
        if(Array.isArray(this.arr)){
            let arrLength = this.arr[0].length;//第一行的元素数量
            firstFor:for (let ind in this.arr){
                if(Array.isArray(this.arr[ind]) && this.arr[ind].length == arrLength){
                    for (let ind_ in this.arr[ind]){
                        if(isRealNum(this.arr[ind][ind_])){

                        }else{
                            this.isMatrixnm = false;
                            break firstFor;
                        }
                    }
                }else{
                    this.isMatrixnm = false;
                    break firstFor;
                }
            }
        }else{
            this.isMatrixnm = false;
        }
    }
    //是否方阵
    verMatrixnn(){
        if(Array.isArray(this.arr)){
            let arrLength = this.arr.length;
            firstFor:for (let ind in this.arr){
                if(Array.isArray(this.arr[ind]) && this.arr[ind].length == arrLength){
                    for (let ind_ in this.arr[ind]){
                        if(isRealNum(this.arr[ind][ind_])){

                        }else{
                            this.isMatrixnn = false;
                            break firstFor;
                        }
                    }
                }else{
                    this.isMatrixnn = false;
                    break firstFor;
                }
            }
        }else{
            this.isMatrixnn = false;
        }
    }

    //矩阵相乘,返回新的矩阵
    matrixMultiply(ma){
        let newArr = [];
        if(ma instanceof Matrix && ma.isMatrixnm){
            if(this.n == ma.m && this.m == ma.n){
                //准备矩阵相乘操作
                for (let i in this.arr){
                    newArr[i] = [];
                    for (let m = 0; m < ma.m;m ++){
                        newArr[i][m] = 0;
                        for(let j in this.arr[i]){
                            newArr[i][m] += this.arr[i][j] * ma.arr[j][m];
                        }
                    }
                }
            }else{
                return null;
                //不符合矩阵相乘的条件
            }
        }else{
            return null;
            //非矩阵不能相乘
        }
        return new Matrix(newArr);//返回结果矩阵
    }

    /**
     * 矩阵*常数
     * @param k
     * @returns {null}
     */
    matrixMultiplyConstant(k){
        console.log(''k'',k)
        let newArr = [];
        if(this.isMatrixnm){
            for (let i = 0; i < this.n; i++) {
                newArr[i] = [];
                for (let j = 0; j < this.m; j++) {
                    newArr[i][j] = this.arr[i][j] * k;
                }
            }
            return newArr;
        }else{
            return null;
        }
    }
    //获得伴随矩阵
    getAdjointMatrix(){
        //先把矩阵转化为行列式
        //首先得是个方阵
        if(this.isMatrixnn){
            let deter_m = new Determinant(this.arr);//得到行列式
            let newArr = [];
            for (let n=0;n<this.n;n++){
                newArr[n] = [];
                for (let m=0;m<this.m;m++){
                    newArr[n].push(deter_m.getAij(n,m))
                }
            }
            return new Matrix(rotateArr(newArr));
        }else{
            return null;
        }
    }
    //得到方阵转化为行列式后的序
    getDeterXu(){
        if(this.isMatrixnn){
            let deter_m = new Determinant(this.arr);//得到行列式
            deter_m.calculate();
            return deter_m.xu;
        }else{
            return null;
        }
    }

    /**
     * 得到逆矩阵
     */
    getNi(){
        return this.getAdjointMatrix().matrixMultiplyConstant(1/(this.getDeterXu()));
    }
}
var matrix = new Matrix([
    [2,1,3],
    [1,1,3],
    [2,2,3],
]);
var matrix1 = new Matrix([
    [2,1,3],
    [1,1,3],
    [2,2,3],
]);
matrix.verMatrixnm();
console.log(matrix.isMatrixnm);
matrix.verMatrixnn();
console.log(matrix.isMatrixnn);
console.log(matrix.matrixMultiply(matrix1))
console.log(''伴随矩阵'',matrix.getAdjointMatrix())
console.log(''方阵的序'',matrix.getDeterXu())
console.log(''方阵*2'',matrix.matrixMultiplyConstant(2))
console.log(''方阵的逆矩阵'',matrix.getNi())

知道了如何求得逆矩阵,接下来就开始画图吧

canvas 画一个三角形,然后 closePath () 封闭图形,用 clip () 在封闭的三角形中话图并且采用计算出来变形矩阵变形

 

9、canvas图形组合方式

9、canvas图形组合方式

globalCompositeOperation ==> 设置图形的组合方式

语法:ctx.globalCompositeOperation = type;

解释:设置或返回如何将一个源(新的)图像绘制到目标(已有)的图像上

        源图像 = 您打算放置到画布上的绘图。

        目标图像 = 您已经放置在画布上的绘图。

.......

详细有道笔记链接>>

 

 

 

android 图形变换效果

android 图形变换效果

在android的界面中显示一张矩形图片,拖动其中一个角,其它三个角不变,实现图片的变形。请问下这个功能的实现思路,或者问下有没有这样的框架?

Canvas---Canvas图像处理、图片查看器、图像缩放功能的实现

Canvas---Canvas图像处理、图片查看器、图像缩放功能的实现

模仿手机图像查看软件用canvas实现一个图像查看器。

目前实现:

利用将图像起点绘制到canvas之外的技术实现了图像的缩放。

包括,图像自适应、图像放大缩小

下一步准备实现:

鼠标拖动图像

截图:

总结下要点与步骤:

要点:图像缩放与自适应要一起做的话,缩放对象实际是canvas,然后图像去对新的canvas做自适应。

步骤:

1.缩放canvas,得到缩放后的canvas的长宽。

2.图像对于缩放后的canvas做自适应,得到图像的长宽。

3.利用旧canvas的中心点与缩放后图像的长宽,计算图像绘制起点。

4.调用drawImage();方法,传入图像,绘制起点,缩放长宽。

本文由 CSDN MIKUScallion 原创,更多canvas案例代码:http://blog.csdn.net/mikuscallion

下面是源代码:

①页面代码

<!DOCTYPE html> <html> <head> <Meta http-equiv="content-type" content="text/html; charset=utf-8" /> <style type="text/css"> canvas{ background-color: black; } .contain{ margin: 0 auto; width: 480px; height: 640px; position: relative; } .scaleSlider{ margin: 0px; padding: 0px; position: absolute; /*旋转元素*/ transform:rotate(-90deg); top: 70px; left:400px; } </style> <script type="text/javascript" src="mikuCanvasImageUtil.js"></script> </head> <body> <div> <input id="scaleSlider"type="range" min="0.25" max="4" step="0.01" value="1" /> <canvas id="canvas" width="480" height="640"> </canvas> </div> </body> <script type = "text/javascript"> var canvas = document.getElementById("canvas"); var context = canvas.getContext("2d"); var scaleSlider = document.getElementById("scaleSlider"); var image = new Image(); var imageUtil = new MikuCanvasImageUtil(); image.src ="miku01.jpg"; image.onload = function(){ //将图像缩放后绘制到canvas中央 imageUtil.drawScaleImageInCenter(context,image); } scaleSlider.onchange = function(){ //清除屏幕 context.clearRect(0,canvas.width,canvas.height); //根据缩放比例将图像在中心缩放 imageUtil.scaleImgInCenter(context,image,scaleSlider.value,false); } </script> </html>

②图像工具代码

var MikuCanvasImageUtil = function(){ } MikuCanvasImageUtil.prototype = { //--------------------------------------------------------------------------------- //图像在canvas中心区域按照给定比例缩放 //noOver表示是,阻止过度缩放?默认true //date:2014-1-31 //author:MIKUScallion scaleImgInCenter:function (context,scale,noOver){ //获得原有canvas长宽 var cw = context.canvas.width; var ch = context.canvas.height; //获得缩放后的canvas长宽 var scaledCw = cw*scale; var scaledCh = ch*scale; //获得相对新canvas的缩放对象 var scaleObj = this.getScaleObj(image,scaledCw,scaledCh,noOver); //获取原有canvas中心坐标 var canvasCenterX = context.canvas.width/2; var canvasCenterY = context.canvas.height/2; //相对于canvas中心点计算,图像顶点 var imageStartPointX = canvasCenterX - scaleObj.width/2; var imageStartPointY = canvasCenterY - scaleObj.height/2; //绘制图像 context.drawImage(image,imageStartPointX,imageStartPointY,scaleObj.width,scaleObj.height); },//将image缩放后绘制到canvas中心 //date:2014-1-31 //author:MIKUScallion drawScaleImageInCenter:function(context,image){ //获取canvas中心坐标 var canvasCenterX = context.canvas.width/2; var canvasCenterY = context.canvas.height/2; //相对原本的canvas获取缩放对象 var scaleObj = this.getScaleObj(image,context.canvas.width,context.canvas.height); //计算图像顶点 var imageStartPointX = canvasCenterX - scaleObj.width/2; var imageStartPointY = canvasCenterY - scaleObj.height/2; //绘制图像 context.drawImage(image,//获得图像相对与一个矩形的缩放对象 //该对象包含:width,height //date:2014-1-31 //author:MIKUScallion getScaleObj:function(image,width,height,noOver){ //默认不能过度缩放 noOver = noOver===undefined? true:noOver; var scaleW,scaleH; var widthLonger = image.width - width; var heightLonger = image.height - height; if(noOver){ //图像无需缩放 if(widthLonger <=0 && heightLonger<=0 ){ scaleW = image.width; scaleH = image.height; return { width:scaleW,height:scaleH }; } } //固定宽度缩放 if(widthLonger >= heightLonger){ scaleW = width; var percent = width/image.width; scaleH = image.height*percent; } //固定长度缩放 else{ scaleH = height; var percent = height/image.height; scaleW = image.width*percent; } //---------------- return { width:scaleW,height:scaleH }; } }
本文由 CSDN MIKUScallion 原创,更多canvas案例代码: http://blog.csdn.net/mikuscallion





关于canvas图形变换canvas图片变形的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于2d 图形变换原理,矩阵换算、9、canvas图形组合方式、android 图形变换效果、Canvas---Canvas图像处理、图片查看器、图像缩放功能的实现的相关信息,请在本站寻找。

本文标签: