Wrap an image around a cylindrical object in HTML5 / JavaScript

谁说胖子不能爱 提交于 2019-11-27 14:12:54

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);

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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!