Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
434 views
in Technique[技术] by (71.8m points)

css - How to match 3D perspective of real photo and object in CSS3 3D transforms

The situation:

In my particular case I have 3D photoframe image and a 2D photo. And I want the 2D photo to match the 3D frame using CSS3 transform functions (Rotate, Scale, Skew).

The problem:

I wasn't able to precisely match the two using manual method aka typing rotation value and watching what it does.

Ideal solution #1

Online visual tool exists that lets you drag corners of the photo (like photoshop does) and It gives you the correct CSS3 transform values.

Ideal solution #2

Non-visual tool exist - same as before but you manually enter 4 point coordinates (image corners) and it gives you the correct CSS3 transform values.

Real solution of this question

If there aren't such tools (my search found none) I would like somebody to try explain the math behind it so I could calculate it myself - If it is even possible?

I prepared JSFiddle demo for you fiddle around: Demo

/* Main issue here */

.transform {
  transform: rotateX(34deg) rotateZ(13deg) rotateY(-10deg) scaleY(1) scaleX(1) skewY(0deg) skewX(0deg) translateY(0px) translateX(20px);
  transform-origin: 50% 0% 0;
}
/* Supporting styles */

.container {
  position: relative;
  width: 500px;
  height: 500px;
}
.frame,
.photo {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}
.photo {
  top: 50px;
  left: 95px;
  right: 65px;
  bottom: 270px;
}
.frame img,
.photo img {
  width: 100%
}
.frame {
  z-index: 2;
}
<div class="container">

  <div class="frame">
    <img src="http://cdn.idesigned.cz/img/cc08acc7b9b08ab53bf935d720210f13.png" />
  </div>

  <div class="photo">
    <div class="transform">
      <img src="https://static.pexels.com/photos/7976/pexels-photo.jpg" />
    </div>
  </div>

</div>
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

If you can use 3-d transforms (such as rotateZ), then you can also provide matrix3d which you can compute from desired point correspondences.

Here's a fiddle: https://jsfiddle.net/szym/03s5mwjv/

I'm using numeric.js to solve a set of 4 linear equations to find the perspective transform matrix that transforms src onto dst. This is essentially the same math as in getPerspectiveTransform in OpenCV.

The computed 2-d perspective transform is a 3x3 matrix using homogeneous coordinates. The CSS matrix3d is a 4x4 matrix using homogeneous coordinates, so we need to add an identity row/column for the z axis. Furthermore, matrix3d is specified in column-major order.

Once you get the matrix3d you can just paste it into your stylesheet. But keep in mind that the matrix is computed assuming (0, 0) as origin, so you also need to set transformOrigin: 0 0.

// Computes the matrix3d that maps src points to dst.
function computeTransform(src, dst) {
  // src and dst should have length 4 each
  var count = 4;
  var a = []; // (2*count) x 8 matrix
  var b = []; // (2*count) vector

  for (var i = 0; i < 2 * count; ++i) {
    a.push([0, 0, 0, 0, 0, 0, 0, 0]);
    b.push(0);
  }

  for (var i = 0; i < count; ++i) {
    var j = i + count;
    a[i][0] = a[j][3] = src[i][0];
    a[i][1] = a[j][4] = src[i][1];
    a[i][2] = a[j][5] = 1;
    a[i][3] = a[i][4] = a[i][5] =
      a[j][0] = a[j][1] = a[j][2] = 0;
    a[i][6] = -src[i][0] * dst[i][0];
    a[i][7] = -src[i][1] * dst[i][0];
    a[j][6] = -src[i][0] * dst[i][1];
    a[j][7] = -src[i][1] * dst[i][1];
    b[i] = dst[i][0];
    b[j] = dst[i][1];
  }

  var x = numeric.solve(a, b);
  // matrix3d is homogeneous coords in column major!
  // the z coordinate is unused
  var m = [
    x[0], x[3],   0, x[6],
    x[1], x[4],   0, x[7],
       0,    0,   1,    0,
    x[2], x[5],   0,    1
  ];
  var transform = "matrix3d(";
  for (var i = 0; i < m.length - 1; ++i) {
    transform += m[i] + ", ";
  }
  transform += m[15] + ")";
  return transform;
}

// Collect the four corners by user clicking in the corners
var dst = [];
document.getElementById('frame').addEventListener('mousedown', function(evt) {
  // Make sure the coordinates are within the target element.
  var box = evt.target.getBoundingClientRect();
  var point = [evt.clientX - box.left, evt.clientY - box.top];
  dst.push(point);

  if (dst.length == 4) {
    // Once we have all corners, compute the transform.
    var img = document.getElementById('img');
    var w = img.width,
        h = img.height;
    var transform = computeTransform(
      [
        [0, 0],
        [w, 0],
        [w, h],
        [0, h]
      ],
      dst
    );
    document.getElementById('photo').style.visibility = 'visible';
    document.getElementById('transform').style.transformOrigin = '0 0';
    document.getElementById('transform').style.transform = transform;
    document.getElementById('result').innerHTML = transform;
  }
});
.container {
  position: relative;
  width: 50%;
}
  
#frame,
#photo {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}
  
#photo {
  visibility: hidden;
}
  
#frame img,
#photo img {
  width: 100%
}
  
#photo {
  opacity: 0.7;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/numeric/1.2.6/numeric.min.js"></script>
  <p id="result">Click the desired top-left, top-right, bottom-right, bottom-left corners
  <div class="container">
    <div id="frame">
      <img src="http://cdn.idesigned.cz/img/cc08acc7b9b08ab53bf935d720210f13.png" />
    </div>

    <div id="photo">
      <div id="transform">
        <img id="img" src="http://placehold.it/350x150" />
      </div>
    </div>

  </div>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...