0

I'm sure the question is confusing already. Essentially, I want to use css to absolutely position a div with perspective to look like it is inside of the laptop screen that I created with svg.

I believe it is possible using some combination of perspective, rotateX(), rotateY() but can't seem to get it right.

See this Codepen

Here's the relevant css:

.container {
    perspective: 500px;
}
.screenContents {
    transform: rotateX(10deg) rotateY(-10deg);
    /* 16:9 ratio to calculate size */
    --scale: 36;
    width: calc(16px * var(--scale));
    height: calc(9px * var(--scale));
    position: absolute;
    top: 30px;
    left: 300px;
}

Html:

<div class='container'>
    <div class='screenContents'>Screen Contents</div>
</div>
<img src='device.svg' />

Some other things I've tried are setting perspective-origin and/or using rotateZ()

SVG Dimensions and Info

In case it proves useful, here are some dimensions of the screen which I calculated from the svg:

area: 1529 sq pixels

(width and height calculated from each corner point)

width: ~500px
height: ~350px

The coordinates of each point of the svg screen which I used to find the height, width, and area:

top left: 382,514
top right: 882,503
bottom left: 312,187
bottom right: 816,151

Also, the svg is just a traced picture of a Google Pixel Go laptop which has a 13.3" screen size and 16:9 ratio

I feel like these dimensions must be included somehow in the final calculations.

Summary

I would like to position a div so that it looks like it is being viewed in the laptop screen. I don't know how to correctly do this and could use some pointers or a mad genius to create a solution for me. Use the codepen to mess around with it.

So, any css gurus out there that might be able to help????

Jake Zeitz
  • 2,429
  • 3
  • 23
  • 43

2 Answers2

1

Change below CSS

.container {
/*   perspective: 500px; */
}

.screenContents {
 transform: perspective(3651px) rotateY(-30deg) rotateX(18deg) rotate(3deg);
 --scale: 37;
 width: calc(16px * var(--scale));
 height: calc(10px * var(--scale));
}
Sameer
  • 4,758
  • 3
  • 20
  • 41
  • nice!! that works, how did you figure it out... trial and error? I would like to replicate your process so I can figure out the same thing with the phone – Jake Zeitz Jan 27 '20 at 05:19
  • I have done this before for my website, inspired/copied from [Stripe](https://stripe.com/en-in/billing) :) – Sameer Jan 27 '20 at 05:29
  • I can't figure out the process, I somehow got it to look like it fits in the phone at one dimension. Then, for mobile I made the phone big and tried again with the perspective stuff but can't get it right. Do you start by messing with perspective, then move on to rotateY, then rotateX, then rotate? – Jake Zeitz Feb 03 '20 at 05:37
0

I've created a demo based on How to match 3D perspective of real photo and object in CSS3 3D transforms by @szym that uses matrix calculation to get exact fit.

I've created a better demo that can be put as a standalone tool (I may create one in the future), where you have 4 points that you can drag. The demo uses jQuery Terminal as a div but you can use any element and fit it into an image. The tool will give you CSS that need to use to fit element into an image. The element should be similar in size (width and height) so it's not stretched or squished.

The basic solution from that answer is this matrix computation:

// Computes the matrix3d that maps src points to dst.
function compute_transform(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 homogenous 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
  ];
  return "matrix3d(" + m.join(',') + ')';
}

// the array should have all 4 points
// of the rectangle on the image
var points = []; 

function transform_terminal() {
    var w = geometry.width + 20,
        h = geometry.height + 20;
    var transform = compute_transform(
        [
            [0, 0],
            [w, 0],
            [w, h],
            [0, h]
        ],
        points
    );
    function format(klass, left, top) {
        return `
.${klass} {
   left: ${left}px;
   top: ${top}px;
}`.trim();
    }
    const point_css = $('.point').map(function() {
        var {left, top} = $(this).position();
        var klas = $(this).attr('class').replace(/point /, ''); 
        return format(klas, left, top);
    }).get().join('\n');
    $('.output pre').html(`
.terminal {
    transform: ${transform};
}
/* optional control points */
${point_css}`.trim())
    term.css({
        '--transform': transform
    });
}

The code uses numeric library to solve a linear equation to get the matrix.

Here is the demo: https://codepen.io/jcubic/pen/PojPbZz

jcubic
  • 61,973
  • 54
  • 229
  • 402