Search code examples
phphtmlcsshtml2canvas

Capturing an image wrapped around a mug with css using html2canvas results cut up


I'm trying to capture an image which i am first generating using CSS.

The CSS generated pic is like this..

enter image description here

and the current html2canvas output is...

enter image description here

Current Code i've tried is as follows...

html2canvas(document.querySelector("#capture"), {
  allowTaint: true
}).then(canvas => {
  document.body.appendChild(canvas)
});
body {
  background: #0000007a;
}

h1 {
  font: small-caps 167% Arial, Helvetica, sans-serif
}

#container {
  margin-left: -56px;
  top: 450px;
  width: 550px;
  -moz-perspective: 800px;
  -webkit-perspective: 800px;
  margin-top: 130px;
}

#container:hover * {
  -moz-animation-play-state: paused;
  -webkit-animation-play-state: paused
}

#frame {
  margin-top: 95px;
  width: 23px;
  -moz-transform-style: preserve-3d;
  -webkit-transform-style: preserve-3d;
  /* translate must be last */
  -moz-transform: rotateX(-1deg) rotateY(45deg) rotateZ(0deg) translate3d(325px, -70px, 50px);
  -webkit-transform: rotateX(-1deg) rotateY(45deg) rotateZ(0deg) translate3d(325px, -70px, 50px)
}

.strip {
  -moz-transform-style: preserve-3d;
  -webkit-transform-style: preserve-3d;
  animation: spin 0.01s forwards;
}

.mastercontainer {
  width: 400px;
  height: 400px;
  display: inline-block;
  overflow: hidden;
}

.strip div {
  position: absolute;
  background: #000 url(http://automation.stickermonkey.shop/codeplayground/images/djmug2.jpg);
  border: solid rgb(145, 87, 0);
  border-width: thin 0;
  height: 275px;
  width: 34px;
  opacity: 1;
}

.strip .a {
  background-position: 0 0;
  -moz-transform: rotateY(0deg) translateZ(124px);
  -webkit-transform: rotateY(0deg) translateZ(124px)
}

.strip .b {
  background-position: 759px 0;
  -moz-transform: rotateY(15deg) translateZ(124px);
  -webkit-transform: rotateY(15deg) translateZ(124px)
}

.strip .c {
  background-position: 726px 0;
  -moz-transform: rotateY(30deg) translateZ(124px);
  -webkit-transform: rotateY(30deg) translateZ(124px)
}

.strip .d {
  background-position: 693px 0;
  -moz-transform: rotateY(45deg) translateZ(124px);
  -webkit-transform: rotateY(45deg) translateZ(124px)
}

.strip .e {
  background-position: 660px 0;
  -moz-transform: rotateY(60deg) translateZ(124px);
  -webkit-transform: rotateY(60deg) translateZ(124px)
}

.strip .f {
  background-position: 627px 0;
  -moz-transform: rotateY(75deg) translateZ(124px);
  -webkit-transform: rotateY(75deg) translateZ(124px)
}

.strip .g {
  background-position: 594px 0;
  -moz-transform: rotateY(90deg) translateZ(124px);
  -webkit-transform: rotateY(90deg) translateZ(124px)
}

.strip .h {
  background-position: 561px 0;
  -moz-transform: rotateY(105deg) translateZ(124px);
  -webkit-transform: rotateY(105deg) translateZ(124px)
}

.strip .i {
  background-position: 528px 0;
  -moz-transform: rotateY(120deg) translateZ(124px);
  -webkit-transform: rotateY(120deg) translateZ(124px)
}

.strip .j {
  background-position: 495px 0;
  -moz-transform: rotateY(135deg) translateZ(124px);
  -webkit-transform: rotateY(135deg) translateZ(124px)
}

.strip .k {
  background-position: 462px 0;
  -moz-transform: rotateY(150deg) translateZ(124px);
  -webkit-transform: rotateY(150deg) translateZ(124px)
}

.strip .l {
  background-position: 429px 0;
  -moz-transform: rotateY(165deg) translateZ(124px);
  -webkit-transform: rotateY(165deg) translateZ(124px)
}

.strip .m {
  background-position: 396px 0;
  -moz-transform: rotateY(180deg) translateZ(124px);
  -webkit-transform: rotateY(180deg) translateZ(124px)
}

.strip .n {
  background-position: 363px 0;
  -moz-transform: rotateY(195deg) translateZ(124px);
  -webkit-transform: rotateY(195deg) translateZ(124px)
}

.strip .o {
  background-position: 330px 0;
  -moz-transform: rotateY(210deg) translateZ(124px);
  -webkit-transform: rotateY(210deg) translateZ(124px)
}

.strip .p {
  background-position: 297px 0;
  -moz-transform: rotateY(225deg) translateZ(124px);
  -webkit-transform: rotateY(225deg) translateZ(124px)
}

.strip .q {
  background-position: 264px 0;
  -moz-transform: rotateY(240deg) translateZ(124px);
  -webkit-transform: rotateY(240deg) translateZ(124px)
}

.strip .r {
  background-position: 231px 0;
  -moz-transform: rotateY(255deg) translateZ(124px);
  -webkit-transform: rotateY(255deg) translateZ(124px)
}

.strip .s {
  background-position: 198px 0;
  -moz-transform: rotateY(270deg) translateZ(124px);
  -webkit-transform: rotateY(270deg) translateZ(124px)
}

.strip .t {
  background-position: 165px 0;
  -moz-transform: rotateY(285deg) translateZ(124px);
  -webkit-transform: rotateY(285deg) translateZ(124px)
}

.strip .u {
  background-position: 132px 0;
  -moz-transform: rotateY(300deg) translateZ(124px);
  -webkit-transform: rotateY(300deg) translateZ(124px)
}

.strip .v {
  background-position: 99px 0;
  -moz-transform: rotateY(315deg) translateZ(124px);
  -webkit-transform: rotateY(315deg) translateZ(124px)
}

.strip .w {
  background-position: 66px 0;
  -moz-transform: rotateY(330deg) translateZ(124px);
  -webkit-transform: rotateY(330deg) translateZ(124px)
}

.strip .x {
  background-position: 33px 0;
  -moz-transform: rotateY(345deg) translateZ(124px);
  -webkit-transform: rotateY(345deg) translateZ(124px)
}

@-moz-keyframes spin {
  from {
    -moz-transform: rotateY(360deg)
  }
  to {
    -moz-transform: rotateY(230deg)
  }
}

@-webkit-keyframes spin {
  from {
    -webkit-transform: rotateY(360deg)
  }
  to {
    -webkit-transform: rotateY(230deg)
  }
}
<script src="https://html2canvas.hertzen.com/dist/html2canvas.js"></script>
<div id="capture" class="mastercontainer">
  <div id="container">
    <div id="frame">
      <div class="strip">
        <div class="a"></div>
        <div class="b"></div>
        <div class="c"></div>
        <div class="d"></div>
        <div class="e"></div>
        <div class="f"></div>
        <div class="g"></div>
        <div class="h"></div>
        <div class="i"></div>
        <div class="j"></div>
        <div class="k"></div>
        <div class="l"></div>
        <div class="m"></div>
        <div class="n"></div>
        <div class="o"></div>
        <div class="p"></div>
        <div class="q"></div>
        <div class="r"></div>
        <div class="s"></div>
        <div class="t"></div>
        <div class="u"></div>
        <div class="v"></div>
        <div class="w"></div>
        <div class="x"></div>
      </div>
    </div>
  </div>
</div>

I have put everything into a jsfiddle here.. https://jsfiddle.net/StickerMonkey/tLqrdey5/4/

EDIT TO ADD: I have also tried foreignObjectRendering: true this gives the correct shape but is a blacked out image.

enter image description here

SECOND EDIT TO ADD; I am also open to suggestions of another way to either wrap a rectangular image round a mug or another way to screenshot or otherwise grab the image i am already generating.


Solution

  • Here's an example using Three.js.

    It creates a scene with a rotating view of the painted mug.

    It also takes a snapshot at 60° rotation in a new canvas.

    It also shows the paint alone in a third canvas with white background.

    //Moved to <script type="module">
    //import * as THREE from 'https://cdn.skypack.dev/three';
    
    function actualCode(THREE) {
      //Variables for rendering
      const renderer = new THREE.WebGLRenderer({
        antialias: true
      });
      const scene = new THREE.Scene();
      const camera = new THREE.PerspectiveCamera(30, 400.0 / 400, 1, 1000);
    
      //Object variables
      let texture;
      let paintedMug;
    
      //Preload image, then trigger rendering
      const loader = new THREE.TextureLoader();
      //Real URL triggers cross origin error in tests:
      // texture = loader.load("http://automation.stickermonkey.shop/codeplayground/images/djmug2.jpg", function(_tex) {
      //Hosting it on local server works:
      // texture = loader.load("./djmug2.jpg", function(_tex) {
      //Example with image hosted from Imgur:
      texture = loader.load("https://i.imgur.com/TQZrUSP.jpeg", function(_tex) {
        // /*Debugging:*/ setTimeout(() => document.body.appendChild(texture.image), 100);
        init();
        snapShot(60);
        renderImageSolo(60);
        render();
      });
      
      function init() {
        //Init scene and camera
        camera.position.set(0, 1.3, 11);
        camera.lookAt(scene.position);
        renderer.setSize(400, 400);
        addTitle("Animated scene:");
        document.body.appendChild(renderer.domElement);
        
        //Set an ambient light
        const light = new THREE.AmbientLight(0xffffff); // soft white light
        scene.add(light);
    
        //Draw white mug
        const muggeom = new THREE.CylinderGeometry(1.5, 1.5, 3.5, 240, 1);
        const mugmaterial = new THREE.MeshStandardMaterial({
          color: "#fff",
        });
        const mug = new THREE.Mesh(muggeom, mugmaterial);
    
        //Draw painting on mug with slightly larger radius
        const paintgeom = new THREE.CylinderGeometry(1.5001, 1.5001, 3.3, 240, 1, true);
        const paintmaterial = new THREE.MeshStandardMaterial({
          map: texture,
        });
        const paint = new THREE.Mesh(paintgeom, paintmaterial);
    
        //Define a group as mug + paint
        paintedMug = new THREE.Group();
        paintedMug.add(mug);
        paintedMug.add(paint);
        //Add group to scene
        scene.add(paintedMug);
      }
    
      function render() {
        try {
          //Draw
          renderer.render(scene, camera);
          //Rotate a bit at each frame
          paintedMug.rotation.y += -.01
          //Trigger next animation frame (default 60fps)
          requestAnimationFrame(render);
        } catch (err) {
          console.log(err, err.message);
        }
      }
    
      function snapShot(angle) {
        const orgAngle = paintedMug.rotation.y;
        //Rotate objects by angle
        paintedMug.rotation.y = angle;
        //render once
        renderer.render(scene, camera);
        //Take still into new canvas
        var canvas = document.createElement("canvas");
        canvas.width = renderer.domElement.width;
        canvas.height = renderer.domElement.height;
        canvas.getContext("2d").drawImage(renderer.domElement, 0, 0);
        canvas.style.marginTop = "1em"; //Space out canvas
        addTitle(`Snapshot at ${angle}°:`);
        document.body.append(canvas);
        //Restore rotation
        paintedMug.rotation.y = orgAngle;
      }
    
      function renderImageSolo(angle) {
        //Init just like main renderer / scene, will use same camera
        const solo_renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true });
        solo_renderer.setSize(renderer.domElement.width, renderer.domElement.height);
        solo_renderer.domElement.style.marginTop = "1em"; //Space out canvas
        addTitle(`Paint alone at ${angle}°:`);
        document.body.appendChild(solo_renderer.domElement);
        const solo_scene = new THREE.Scene();
        //Set an ambient light
        const light = new THREE.AmbientLight(0xffffff); // soft white light
        solo_scene.add(light);
    
        //Draw painting alone
        const paintgeom = new THREE.CylinderGeometry(1.5, 1.5, 3.3, 240, 1, true);
        const paintmaterial = new THREE.MeshStandardMaterial({
          map: texture,
        });
        const paint = new THREE.Mesh(paintgeom, paintmaterial);
        //Add paint to scene
        solo_scene.add(paint);
        //Rotate paint by angle
        paint.rotation.y = angle
        //Draw result
        solo_scene.background = new THREE.Color(0xffffff);
        solo_renderer.render(solo_scene, camera);
      }
    
      function addTitle(title) {
        const h2 = document.createElement('h2');
        h2.innerText = title;
        document.body.appendChild(h2);
      }
    }
    <!-- Below code is just a workaround for ES6 imports in SO snippet -->
    <script type="module"> //Forced to put import in HTML section, as SO snippet does not support ES6 module loading
    import * as THREE from 'https://cdn.skypack.dev/three';
    document.addEventListener("DOMContentLoaded", _e => actualCode(THREE));
    </script>

    There were problems using your original image from another server due to CORS policy. I've setup the sample with a copy hosted on Imgur, but code for your original location is also included (see comments).