Flip images at 30 degrees?

前端 未结 3 551
栀梦
栀梦 2020-12-22 13:30

In canvas I know that I can flip images horizontally and vertically by doing this:

vc.scale(-1, -1)

But is there some way to flip an image

相关标签:
3条回答
  • 2020-12-22 13:42

    Yes, you can accomplish this using the rotate() method of canvas :

    var ctx = document.getElementById("myCanvas").getContext("2d");
    var img = new Image();
    img.src = "http://photos.the-scientist.com/articleImages/48000/48607-1-t.jpg";
    img.onload = function() {
        ctx.rotate(30 * Math.PI / 180);
        ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
        ctx.scale(1, -1);
        ctx.drawImage(img, -65, 65);
    }
    <canvas id="myCanvas" width="300" height="300">

    0 讨论(0)
  • 2020-12-22 13:46

    Rotate image 30 degrees, flip image, rotate -30 degrees.

    0 讨论(0)
  • 2020-12-22 13:54

    Mirror along line

    You can mirror along any line using the following function. It sets the transform so that you can render your scene as normal and it will be mirrored along the line.

    function mirrorTransformLine(line){
        var x1 = line.x1;
        var y1 = line.y1;
        var x2 = line.x2;
        var y2 = line.y2;
        if(x1 > x2){  // to save some messing about with signs make the line always from left to right
            x2 = line.x1;
            y2 = line.y1;
            x1 = line.x2;
            y1 = line.y2;
        }
        var x = x2-x1;  // get the vector from line start to end
        var y = y2-y1; 
        var ox = -x1;  // get vector from line start to origin
        var oy = -y1;
        var len = Math.hypot(x,y); // get the length of the line
        var nx = x / len;  // normalise the line
        var ny = y / len;
    
    
        // We must find the mirrored origin
        // get the unit distance along the line where the mirrored y axis intercepts
        var u = (ox * x + oy * y)/(y * y + x * x);
        var dx = u * len; // get the x dist of the mirrored origin    
        var dy = Math.hypot(x1 + x * u, y1 + y * u); // get the mirrored y axis distance from line
    
        // the above code does not account for the direction of the origin. We don't know if its above or below the line
        // we can get the cross product of the mirror line and the vector to the origin. This will give us the sign (direction) to the origin
        dy *=  Math.sign(ox * y - oy * x); // flip the y distance if needed
        // calculate the  the location of the mirrored origin
        var mox = dx * nx - dy * ny + x1;
        var moy = dx * ny + dy * nx + y1;
        
    
        // Find the angle of the line to the x axis
        // var cross = 1 * ny - 0 * nx; // cross product give the sin of the angle between the line and the x axis
        // As the cross product is with 1,0 we can simplify
        var ang = Math.asin(ny); // with ny the cross product
        
        // now find the mirrored angle which is 2 times the angle to the x axis
        // use that angle to get the new x axis
        var axx = Math.cos(ang*2);
        var axy = Math.sin(ang*2);
        
        // this represents the x axis of the transform
        // you would normally rotate it clockwise 90 for the y axis 
        // to mirror its anticlockwise
        ctx.setTransform(axx,axy,axy,-axx,mox,moy);
    }
    

    Thus if you have a line at 30 deg

    var line = {
         x1 : 100,
         y1 : 100,
         x2 : 100 + Math.cos((1/6)* Math.PI), // (1/6) *PI is 30 deg
         y2 : 100 + Math.sin((1/6)* Math.PI),
    }
    
    // draw the scene
    ctx.fillRect(50,50,50,100);
    mirrorTransformLine(line); // create the mirror transformation
    ctx.fillRect(50,50,50,100); // draw the scene again this time its is mirrored
    ctx.setTransform(1,0,0,1,0,0); // restore the transform to default
    

    As a demo.

    Use mouse to drag ends of red line to see it create the mirror transform. The scene is draw twice using the same coordinates. I have also clipped to the line so that the mirror does not overlap.

    function mirrorTransformLine(line){
        var x1 = line.x1;
        var y1 = line.y1;
        var x2 = line.x2;
        var y2 = line.y2;
        if(x1 > x2){  // to save some messing about with signs make the line always from left to right
            x2 = line.x1;
            y2 = line.y1;
            x1 = line.x2;
            y1 = line.y2;
        }
        var x = x2-x1;  // get the vector from line start to end
        var y = y2-y1; 
        var ox = -x1;  // get vector from line start to origin
        var oy = -y1;
        var len = Math.hypot(x,y); // get the length of the line
        var nx = x / len;  // normalise the line
        var ny = y / len;
    
    
        // We must find the mirrored origin
        // get the unit distance along the line where the mirrored y axis intercepts
        var u = (ox * x + oy * y)/(y * y + x * x);
        var dx = u * len; // get the x dist of the mirrored origin    
        var dy = Math.hypot(x1 + x * u, y1 + y * u); // get the mirrored y axis distance from line
    
        // the above code does not acount for the direction of the origin. We dont know if its above or below the line
        // we can get the cross product of the mirror line and the vector to the origin. This will give us the sign (direction) to the origin
        dy *=  Math.sign(ox * y - oy * x);
        // calculate the  the location of the mirrored origin
        var mox = dx * nx - dy * ny + x1;
        var moy = dx * ny + dy * nx + y1;
        
    
        // Find the angle of the line to the x axis
        // var cross = 1 * ny - 0 * nx; // cross product give the sin of the angle between the line and the x axis
        // As the cross product is with 1,0 we can simplify
        var ang = Math.asin(ny); // with ny the cross product
        
        // now find the mirrored angle which is 2 time the angle to the x axis
        // use that angle to get the new x axis
        var axx = Math.cos(ang*2);
        var axy = Math.sin(ang*2);
        
        // this represents the x axis of the transform
        // you would normally rotate it clockwise 90 for the y axis 
        // to mirror its anticlockwise
        ctx.setTransform(axx,axy,axy,-axx,mox,moy);
    }
    
    
    function clipToLine(line){
        var x =line.x2-line.x1;  // get the vector from line start to end
        var y =line.y2-line.y1; 
    
        var len = Math.hypot(x,y); // get the length of the line
        var nx = x / len;  // normalise the line
        var ny = y / len;
        // from 1000 px before start to 1000 px after end create dividing line
        ctx.beginPath();
        ctx.moveTo(line.x1 - nx * 1000, line.y1 - ny * 1000);
        ctx.lineTo(line.x2 + nx * 1000, line.y2 + ny * 1000);
        ctx.lineTo(line.x2 + nx * 1000 - ny * 1000, line.y2 + ny * 1000 + nx * 1000);
        ctx.lineTo(line.x1 - nx * 1000 - ny * 1000, line.y1 - ny * 1000 + nx * 1000);
        ctx.clip();
        
        
    }
    
    var line;
    var onResize = function(){  // this is called at start
        line = {
            x1 : 10, 
            y1 : canvas.height /2,
            x2 : canvas.width -10,
            y2 : canvas.height /2,
        };
        ctx.font = Math.floor(canvas.height /10) + "px arial";
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
    }
    function drawLine(line,sCol,width){
        ctx.strokeStyle = sCol;
        ctx.lineWidth = width;
        ctx.beginPath();
        ctx.moveTo(line.x1,line.y1);
        ctx.lineTo(line.x2,line.y2);
        ctx.stroke();
    }
    function drawCircle(x,y,r,fCol,sCol,width){
        ctx.fillStyle = fCol;
        ctx.strokeStyle = sCol;
        ctx.lineWidth = width;
        ctx.beginPath();
        ctx.arc(x,y,r,0,Math.PI*2);
        if(fCol) {ctx.fill()}
        if(sCol) {ctx.stroke()}
        
    }
    var dragging = 0;
    function display() { 
        ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
        ctx.globalAlpha = 1; // reset alpha
        ctx.clearRect(0, 0, w, h);
        var dist = Math.hypot(line.x1 - mouse.x, line.y1 - mouse.y);
        var dist1 = Math.hypot(line.x2 - mouse.x, line.y2 - mouse.y);
        var col1 = "blue";
        var col2 = "blue";
        if(dragging){
            if(dragging === 1){
                line.x1 = mouse.x;
                line.y1 = mouse.y;
                canvas.style.cursor = "move";
            }else{
                line.x2 = mouse.x;
                line.y2 = mouse.y;
                canvas.style.cursor = "move";
            }
            if(mouse.buttonRaw !== 1){
                dragging = 0;
                canvas.style.cursor = "default";
            }
        }else if(dist1 < dist && dist1 < 40){
            col2 = "red";
            canvas.style.cursor = "move";
            if(mouse.buttonRaw === 1){
                dragging = 2;
            }
        }else
        if(dist < dist1 && dist < 40){
            col1 = "red"
            canvas.style.cursor = "move";
            if(mouse.buttonRaw === 1){
                dragging = 1;
            }
        }else{
            canvas.style.cursor = "default";
            
        }
        ctx.save();
        clipToLine(line);
        drawCircle(canvas.width /2, canvas.height / 4 , canvas.height / 5, "Green","Blue",10);
        drawCircle(canvas.width /2, canvas.height * (3/4) , canvas.height / 5, "Blue","Green",10);
        ctx.fillStyle = "black";
        ctx.fillText("Mirror about the line.",canvas.width / 2, canvas.height / 2);
        ctx.restore();
        mirrorTransformLine(line);
        ctx.save();
        clipToLine(line);
        drawCircle(canvas.width /2, canvas.height / 4 , canvas.height / 5, "Green","Blue",10);
        drawCircle(canvas.width /2, canvas.height * (3/4) , canvas.height / 5, "Blue","Green",10);
        ctx.fillStyle = "#444";
        ctx.fillText("Mirror about the line.",canvas.width / 2, canvas.height / 2);
        ctx.restore();
    
        drawLine(line,"red",4);
        drawCircle(line.x1,line.y1,10,"white",col1,4);
        drawCircle(line.x2,line.y2,10,"white",col2,4);
    
    }
    
    
    
    
    
    
    
    
    
    //===========================================================================
    // Boilerplat code from here down not part of answer
    var w, h, cw, ch, canvas, ctx, mouse, globalTime = 0, firstRun = true;
    
    
    
    ;(function(){
        const RESIZE_DEBOUNCE_TIME = 100;
        var  createCanvas, resizeCanvas, setGlobals, resizeCount = 0;
        createCanvas = function () {
            var c,
            cs;
            cs = (c = document.createElement("canvas")).style;
            cs.position = "absolute";
            cs.top = cs.left = "0px";
            cs.zIndex = 1000;
            document.body.appendChild(c);
            return c;
        }
        resizeCanvas = function () {
            if (canvas === undefined) {
                canvas = createCanvas();
            }
            canvas.width = innerWidth;
            canvas.height = innerHeight;
            ctx = canvas.getContext("2d");
            if (typeof setGlobals === "function") {
                setGlobals();
            }
            if (typeof onResize === "function") {
                if(firstRun){
                    onResize();
                    firstRun = false;
                }else{
                    resizeCount += 1;
                    setTimeout(debounceResize, RESIZE_DEBOUNCE_TIME);
                }
            }
        }
        function debounceResize() {
            resizeCount -= 1;
            if (resizeCount <= 0) {
                onResize();
            }
        }
        setGlobals = function () {
            cw = (w = canvas.width) / 2;
            ch = (h = canvas.height) / 2;
        }
        mouse = (function () {
            function preventDefault(e) {
                e.preventDefault();
            }
            var mouse = {
                x : 0,
                y : 0,
                w : 0,
                alt : false,
                shift : false,
                ctrl : false,
                buttonRaw : 0,
                over : false,
                bm : [1, 2, 4, 6, 5, 3],
                active : false,
                bounds : null,
                crashRecover : null,
                mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
            };
            var m = mouse;
            function mouseMove(e) {
                var t = e.type;
                m.bounds = m.element.getBoundingClientRect();
                m.x = e.pageX - m.bounds.left;
                m.y = e.pageY - m.bounds.top;
                m.alt = e.altKey;
                m.shift = e.shiftKey;
                m.ctrl = e.ctrlKey;
                if (t === "mousedown") {
                    m.buttonRaw |= m.bm[e.which - 1];
                } else if (t === "mouseup") {
                    m.buttonRaw &= m.bm[e.which + 2];
                } else if (t === "mouseout") {
                    m.buttonRaw = 0;
                    m.over = false;
                } else if (t === "mouseover") {
                    m.over = true;
                } else if (t === "mousewheel") {
                    m.w = e.wheelDelta;
                } else if (t === "DOMMouseScroll") {
                    m.w = -e.detail;
                }
                if (m.callbacks) {
                    m.callbacks.forEach(c => c(e));
                }
                e.preventDefault();
            }
            m.addCallback = function (callback) {
                if (typeof callback === "function") {
                    if (m.callbacks === undefined) {
                        m.callbacks = [callback];
                    } else {
                        m.callbacks.push(callback);
                    }
                }
            }
            m.start = function (element) {
                if (m.element !== undefined) {
                    m.removeMouse();
                }
                m.element = element === undefined ? document : element;
                m.mouseEvents.forEach(n => {
                    m.element.addEventListener(n, mouseMove);
                });
                m.element.addEventListener("contextmenu", preventDefault, false);
                m.active = true;
            }
            m.remove = function () {
                if (m.element !== undefined) {
                    m.mouseEvents.forEach(n => {
                        m.element.removeEventListener(n, mouseMove);
                    });
                    m.element.removeEventListener("contextmenu", preventDefault);
                    m.element = m.callbacks = undefined;
                    m.active = false;
                }
            }
            return mouse;
        })();
    
    
    
        function update(timer) { // Main update loop
            if(ctx === undefined){
                return;
            }
            globalTime = timer;
            display(); // call demo code
            requestAnimationFrame(update);
        }
        setTimeout(function(){
            resizeCanvas();
            mouse.start(canvas, true);
            window.addEventListener("resize", resizeCanvas);
            requestAnimationFrame(update);
        },0);
    })();
    /** SimpleFullCanvasMouse.js end **/

    0 讨论(0)
提交回复
热议问题