Wrap an image around a cylindrical object in HTML5 / JavaScript

后端 未结 2 1887

I want to wrap an image around a cylindrical object like mugs in a web app, like so

This will likely be a base image (e.g. a jpeg image of a mug) containin

相关标签:
2条回答
  • Simple cylinder wrap with Canvas 2D

    Very simple example using sin and cos to create the curved map. The images is cut into strips approx 1 pixel wide then rendered as half a squashed circle. As perspective is a linear effect related to distance I also add a small amount of perspective by scaling up in the y direction depending on sin(angle) (where angle = 0 on the left Math.PI / 2 at the forward center).

    These two demos are animated just to show that it is not a slow process, but compared to webGL it is a snail. If you use such a method, don't make it realtime or you will chew up the batteries of mobile devices. Realtime 3D should be done with webGL

    Basic wrap

    var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}
    
    var canvas = createImage(400,400);
    var ctx = canvas.ctx;
    document.body.appendChild(canvas)
    ctx.clearRect(0,0,500,500)
    var image = createImage(400,200);
    image.ctx.font = "60px arial";
    image.ctx.textAlign = "center";
    image.ctx.fillStyle = "#7F5";
    image.ctx.fillRect(0,0,image.width,image.height)
    image.ctx.fillStyle = "white";
    image.ctx.fillText("Wrap around",200,60)
    image.ctx.fillText("Some images",200,140)
    
    
    function draw(ang,tilt, perspective){
        var step = 1/(Math.max(image.width,400));
        for(var i = 0; i < 1; i += step){
            var a = i * Math.PI;
            var a1 = (i+ step*2) * Math.PI ;
            var ix = i * image.width*1.2;
            var iw = step * image.width*1.2;
            a += ang * Math.PI * 2;
            a1 += ang * Math.PI * 2;
            a = Math.PI -a;
            a1 = Math.PI -a1;
            var x = canvas.width * 0.5;
            var y = canvas.height * 0.1;
            
            var x1 = x + Math.cos(a1) * 110;
            var y1 = y + Math.sin(a) * tilt;
            x += Math.cos(a) * 110;
            y += Math.sin(a) * tilt;
            var s = Math.sin(a);
            var s1 = Math.sin(a1);
            if(s > 0 || s1 > 0){
                ctx.drawImage(image,ix,0,iw,image.height,x1,y- s * perspective*0.5,x-x1,200 + s * perspective)
            }
            
            
        }
    }
    var w = canvas.width;
    var h = canvas.height;
    
    
    
    
    // main update function
    function update(timer){
        ctx.setTransform(1,0,0,1,0,0); // reset transform
        ctx.globalAlpha = 1;           // reset alpha
        ctx.fillStyle = "black"
        ctx.fillRect(0,0,w,h);
        draw(timer / 2000, 40,30)
        requestAnimationFrame(update);
    }
    requestAnimationFrame(update);

    Add lighting.

    To extend it a little further you can add overlays to do lighting. I could not find a public domain image of a white cup so I used the same function to render the lighting (A few simple gradients) onto and image. Then for the final output I render the overlay as a backing image, then the text, then two passes again with the shading image, first darken with "multiply", then a soft highlight with "lighten"

    var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}
    
    var canvas = createImage(400,400);
    var ctx = canvas.ctx;
    document.body.appendChild(canvas)
    ctx.clearRect(0,0,500,500)
    var image = createImage(400,200);
    image.ctx.font = "60px arial";
    image.ctx.textAlign = "center";
    image.ctx.fillStyle = "#999";
    image.ctx.fillRect(0,10,image.width,image.height-20)
    image.ctx.fillStyle = "white";
    image.ctx.fillText("Wrap around",200,60)
    image.ctx.fillText("Some images",200,140)
    
    //---------------------------------------------------------------------
    // create shading map
    var shading = createImage(400,200);
    // left to right shading
    var g1 = shading.ctx.createLinearGradient(0,0,400,0);
    g1.addColorStop(0,"rgba(245,245,245,1)");
    g1.addColorStop(0.05,"rgba(255,255,255,1)");
    g1.addColorStop(0.5,"rgba(230,230,230,1)");
    g1.addColorStop(0.95,"rgba(255,255,255,1)");
    g1.addColorStop(1,"rgba(245,245,245,1)");
    shading.ctx.fillStyle = g1;
    shading.ctx.fillRect(0,0,400,200);
    
    // bottom to top shading
    var g = shading.ctx.createLinearGradient(0,0,0,200);
    g.addColorStop(1,"rgba(200,200,200,1)");
    g.addColorStop(0.95,"rgba(200,200,200,0.4)");
    g.addColorStop(0,"rgba(255,255,255,0.0)");
    shading.ctx.globalCompositeOperation = "multiply";
    shading.ctx.fillStyle = g;
    shading.ctx.fillRect(0,0,400,200);
    
    
    var g = shading.ctx.createRadialGradient(0,-100,100,0,-100,200);
    g.addColorStop(0,"rgba(200,200,200,1)");
    g.addColorStop(0.95,"rgba(255,255,255,1)");
    g.addColorStop(1,"rgba(255,255,255,0)");
    shading.ctx.fillStyle = g;
    shading.ctx.globalCompositeOperation = "screen";
    shading.ctx.setTransform(1.4,0,0,1,200,0);
    shading.ctx.beginPath();
    shading.ctx.arc(0,-100,200,0,Math.PI * 2);
    shading.ctx.globalAlpha = 0.5;
    shading.ctx.fill();
    shading.ctx.setTransform(1,0,0,1,0,0);
    shading.ctx.fillStyle = g1;
    shading.ctx.fillRect(0,0,400,200);
    
    var overlay = createImage(400,400);
    draw(shading,overlay.ctx,0, 40,30,110,200,1);
    
    function draw(image,ctx,ang,tilt, perspective, width, height,stretch){
        var step = 1/(Math.max(image.width,400));
        for(var i = 0; i < 1; i += step){
            var a = i * Math.PI;
            var a1 = (i+ step*2) * Math.PI ;
            var ix = i * image.width*stretch;
            var iw = step * image.width*stretch;
            a += ang * Math.PI * 2;
            a1 += ang * Math.PI * 2;
            a = Math.PI -a;
            a1 = Math.PI -a1;
            var x = canvas.width * 0.5;
            var y = canvas.height * 0.1;
            
            var x1 = x + Math.cos(a1) * width;
            var y1 = y + Math.sin(a) * tilt;
            x += Math.cos(a) * width;
            y += Math.sin(a) * tilt;
            var s = Math.sin(a);
            var s1 = Math.sin(a1);
            if(s > 0 || s1 > 0){
                ctx.drawImage(image,ix,0,iw,image.height,x1,y- s * perspective*0.5,(x-x1-1),height + s * perspective)
            }
            
            
        }
    }
    var w = canvas.width;
    var h = canvas.height;
    
    // main update function
    function update1(timer){
        ctx.setTransform(1,0,0,1,0,0); // reset transform
        ctx.globalAlpha = 1;           // reset alpha
        ctx.fillStyle = "black"
        ctx.fillRect(0,0,w,h);
        ctx.drawImage(overlay,0,0);
        draw(image,ctx,timer / 4000, 40,30,110,200,1)
        ctx.globalCompositeOperation = "multiply";
        ctx.drawImage(overlay,0,0);
        ctx.globalAlpha = 0.2
        ctx.globalCompositeOperation = "lighten";
        ctx.drawImage(overlay,0,0);
        ctx.globalCompositeOperation = "source-over";
        requestAnimationFrame(update1);
    }
    
     requestAnimationFrame(update1);

    0 讨论(0)
  • 2020-11-30 09:00

    function canvas1() {
      var canvas = document.getElementById("canvas1");
      var ctx = canvas.getContext("2d");
    
      var productImg = new Image();
      productImg.onload = function() {
        var iw = productImg.width;
        var ih = productImg.height;
        console.log("height");
    
        canvas.width = iw;
        canvas.height = ih;
    
        ctx.drawImage(productImg, 0, 0, productImg.width, productImg.height,
          0, 0, iw, ih);
        loadUpperIMage()
      };
    
      productImg.src = "http://res.cloudinary.com/pussyhunter/image/upload/c_scale,f_auto,h_350/left_handle_cup_i7ztfs.jpg"
    
    
      function loadUpperIMage() {
        var img = new Image();
    
    
        img.src = "http://res.cloudinary.com/pussyhunter/image/upload/v1488184107/500_F_97150423_M13q2FeAUZxxIx6CaPixHupprmyiVVli_skh6fe.jpg"
        img.onload = function() {
    
          var iw = img.width;
          var ih = img.height;
    
          var xOffset = 102, //left padding
            yOffset = 110; //top padding
    
          //alert(ih)
          var a = 75.0; //image width
          var b = 10; //round ness
    
          var scaleFactor = iw / (4 * a);
    
          // draw vertical slices
          for (var X = 0; X < iw; X += 1) {
            var y = b / a * Math.sqrt(a * a - (X - a) * (X - a)); // ellipsis equation
            ctx.drawImage(img, X * scaleFactor, 0, iw / 9, ih, X + xOffset, y + yOffset, 1, 174);
          }
        };
      }
    
    };
    
    function canvas2() {
    
      var canvas = document.getElementById("canvas2");
      var ctx = canvas.getContext("2d");
    
      var productImg = new Image();
      productImg.onload = function() {
        var iw = productImg.width;
        var ih = productImg.height;
        console.log("height");
    
        canvas.width = iw;
        canvas.height = ih;
    
        ctx.drawImage(productImg, 0, 0, productImg.width, productImg.height,
          0, 0, iw, ih);
        loadUpperIMage()
      };
    
    
      productImg.src = "http://res.cloudinary.com/pussyhunter/image/upload/h_350/canter_handle_cup_xyxhdd.jpg"
    
      function loadUpperIMage() {
        var img = new Image();
    
        img.src = "http://res.cloudinary.com/pussyhunter/image/upload/v1488184107/500_F_97150423_M13q2FeAUZxxIx6CaPixHupprmyiVVli_skh6fe.jpg"
    
        img.onload = function() {
    
          var iw = img.width;
          var ih = img.height;
    
          // alert(iw)
    
          var xOffset = 101, //left padding
            yOffset = 110; //top padding
    
          var a = 75.0; //image width
          var b = 10; //round ness
    
          var scaleFactor = iw / (4 * a);
    
          // draw vertical slices
          for (var X = 0; X < iw; X += 1) {
            var y = b / a * Math.sqrt(a * a - (X - a) * (X - a)); // ellipsis equation
            ctx.drawImage(img, X * scaleFactor, 0, iw / 3, ih, X + xOffset, y + yOffset, 1, 174);
    
          }
        };
      }
    
    };
    
    function canvas3() {
    
      var canvas = document.getElementById("canvas3");
      var ctx = canvas.getContext("2d");
    
      var productImg = new Image();
      productImg.onload = function() {
        var iw = productImg.width;
        var ih = productImg.height;
    
        canvas.width = iw;
        canvas.height = ih;
    
        ctx.drawImage(productImg, 0, 0, productImg.width, productImg.height,
          0, 0, iw, ih);
        loadUpperIMage()
      };
    
      productImg.src = "http://res.cloudinary.com/pussyhunter/image/upload/h_350/right_handle_cup_dsdhr7.jpg"
    
    
      function loadUpperIMage() {
        var img = new Image();
    
        img.src = "http://res.cloudinary.com/pussyhunter/image/upload/v1488184107/500_F_97150423_M13q2FeAUZxxIx6CaPixHupprmyiVVli_skh6fe.jpg"
    
        img.onload = function() {
    
          var iw = img.width;
          var ih = img.height;
    
          //alert(iw)
    
          var xOffset = 102, //left padding
            yOffset = 110; //top padding
    
          var a = 75.0; //image width
          var b = 10; //round ness
    
          var scaleFactor = iw / (3 * a);
    
          // draw vertical slices
          for (var X = 0; X < iw; X += 1) {
            var y = b / a * Math.sqrt(a * a - (X - a) * (X - a)); // ellipsis equation
            ctx.drawImage(img, X * scaleFactor, 0, iw / 1.5, ih, X + xOffset, y + yOffset, 1, 174);
          }
        };
      }
    
    };
    
    setTimeout(function() {
      canvas1()
    }, 1000);
    setTimeout(function() {
      canvas2()
    }, 2000);
    setTimeout(function() {
      canvas3()
    }, 3000);
    <!DOCTYPE html>
    <html>
    
    <head>
      <script data-require="jquery@*" data-semver="2.1.4" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
      <link rel="stylesheet" href="style.css" />
    
      <script src="script.js"></script>
    </head>
    
    <body>
    
      <div>
        <canvas id="canvas1"></canvas>
      </div>
    
      <div>
        <canvas id="canvas2"></canvas>
      </div>
    
      <div>
        <canvas id="canvas3"></canvas>
      </div>
    
    
    </body>
    
    </html>

    Note : Just Use these points to calibrate

    var scaleFactor = iw / (4*a); //EDIT 4*a TO 6*a
    
    ctx.drawImage(img, X * scaleFactor, 0, iw/3, ih, X + xOffset, y + yOffset, 1, 174); //EDIT  iw/3 TO iw/4
    
    0 讨论(0)
提交回复
热议问题