16

Using divs or canvas, the goal is to completely fill a device frame with an image/screenshot, but without bleeding past the frame.

Screenshot 1 illustrates an image snugly fitting inside a frame. Screenshot 2 illustrates an image bleeding past the frame.

The first challenge is the image/screenshot may vary in aspect ratio, i.e., different size images may get used. For instance, one image may be 1242x2688 and another image may be 1440x2960. No matter the aspect ratio, the screenshot should fill the frame but not bleed past its edges.

The second challenge is they are often scaled in CSS with transform: scale(x) to ~25%, and the rounding behavior of browsers generates pixel-size gaps at this scale. These gaps, however, vanish when the scale is restored to 100%.

We have tried two options. Both have flaws.

Both make the image/screenshot a child div of the frame div.

Option 1: Padding Values

We used "padding" values to adjust the width, height, and position of the child div (i.e., screenshot) so it fits inside its parent, the frame. However, images with different aspect ratios may bleed past the frame or not fill the frame.

Option 2: Clipping Path

We used clipping paths to represent the area inside the frame, but sometimes gaps were visible depending on the CSS scale value. We cannot allow gaps.

The gap problem is illustrated in the code and Codepen below.

Is there another option?

Codepen (illustrates gaps): https://codepen.io/anon/pen/yWXvJE

.colorClassProxy {
  display: none
}

.itemBox, .itemBox > * {
    position: absolute;
    box-sizing: border-box;
}

.backgroundColorBox, .backgroundGraphicBox, .foregroundBox, .frameBox {
    width: 100%;
    height: 100%;
    background-size: contain;
    background-color: transparent;
    background-position: center;
    background-repeat: no-repeat;
    pointer-events: none;
}
<div class="itemBox graphic" id="NZW2Hmn4nVgb" style="width: 173px; height: 364px; top: 86px; left: 111px;"><div class="backgroundColorBox" style="width: 163px; height: 335px; top: 17px; left: 4px; -webkit-mask-image: url(&quot;data:image/svg+xml;charset=utf8,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%2241.7%20142.4%201445.2%202965.9%22%20preserveAspectRatio=%22none%22%3E%3Cpath%20d=%22M1359.3,3105.3h-1190c-68.8,0-124.6-53.2-124.6-118.9V264.3c0-65.7,55.8-118.9,124.6-118.9h1190%20%20c68.8,0,124.6,53.3,124.6,118.9l0,2722.1C1484,3052.1,1428.2,3105.4,1359.3,3105.3L1359.3,3105.3z%22%20style=%22fill:%20white;%20stroke:%20white;%20stroke-width:%206;%22/%3E%3C/svg%3E&quot;); background-size: cover;"></div><div class="backgroundGraphicBox" style="width: 163px; height: 335px; top: 17px; left: 4px; -webkit-mask-image: url(&quot;data:image/svg+xml;charset=utf8,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%2241.7%20142.4%201445.2%202965.9%22%20preserveAspectRatio=%22none%22%3E%3Cpath%20d=%22M1359.3,3105.3h-1190c-68.8,0-124.6-53.2-124.6-118.9V264.3c0-65.7,55.8-118.9,124.6-118.9h1190%20%20c68.8,0,124.6,53.3,124.6,118.9l0,2722.1C1484,3052.1,1428.2,3105.4,1359.3,3105.3L1359.3,3105.3z%22%20style=%22fill:%20white;%20stroke:%20white;%20stroke-width:%206;%22/%3E%3C/svg%3E&quot;); background-size: cover;"></div><div class="foregroundBox" style="width: 163px;height: 335px;top: 17px;left: 4px;-webkit-mask-image: url(&quot;data:image/svg+xml;charset=utf8,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%2241.7%20142.4%201445.2%202965.9%22%20preserveAspectRatio=%22none%22%3E%3Cpath%20d=%22M1359.3,3105.3h-1190c-68.8,0-124.6-53.2-124.6-118.9V264.3c0-65.7,55.8-118.9,124.6-118.9h1190%20%20c68.8,0,124.6,53.3,124.6,118.9l0,2722.1C1484,3052.1,1428.2,3105.4,1359.3,3105.3L1359.3,3105.3z%22%20style=%22fill:%20white;%20stroke:%20white;%20stroke-width:%206;%22/%3E%3C/svg%3E&quot;);background-size: cover;background-image: url(https://uce9d8d4a8a6c69a057e9a584674.dl.dropboxusercontent.com/cd/0/inline/AhH2Z_q_te_Yu3IKd2cdiB0XMhJEzRdO4KP686rb3VdE1hxeIamOskpIoAhrxKegUAxERpAIyX3g0VABD7EU5S4JWe9X_Q1zdaS2hsoD_SpI4w/file#);"></div><div class="frameBox" data-natural-width="1528.64" data-natural-height="3224.29" style="background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTI4LjY0IiBoZWlnaHQ9IjMyMjQuMjkiIHZpZXdCb3g9IjAgMCAxNTI4LjY0IDMyMjQuMjkiIGRhdGEtaHAtY29sb3ItcHJlcGFyZWQ9InRydWUiIHN0eWxlPSItLWhwLWNvbG9yLTluYXo1Z0JWLTE6cmdiKDI1NSwgMjU1LCAyNTUpOyAtLWhwLWNvbG9yLTluYXo1Z0JWLTI6dXJsKFwjY29sb3ItOW5hejVnQlYtMik7Ij48cmFkaWFsR3JhZGllbnQgaWQ9ImNvbG9yLTluYXo1Z0JWLTIiIGRhdGEtY3NzPSJyYWRpYWwtZ3JhZGllbnQocmdiKDY3LCA2NywgNjcpLCByZ2IoMCwgMCwgMCkpIiBjeD0iNTAlIiBjeT0iNTAlIiByPSI3MC43MSUiPjxzdG9wIHN0b3AtY29sb3I9InJnYig2Nyw2Nyw2NykiIG9mZnNldD0iMCUiLz48c3RvcCBzdG9wLWNvbG9yPSJyZ2IoMCwwLDApIiBvZmZzZXQ9IjEwMCUiLz48L3JhZGlhbEdyYWRpZW50Pjx0aXRsZT5Bc3NldCAyPC90aXRsZT48cGF0aCBkPSJNODU3Ljc4LDcyLjY4SDY3MC44NmMtNy42NCwwLTEzLjg1LDUuOTEtMTMuODUsMTMuMjFzNi4yMSwxMy4yMSwxMy44NSwxMy4yMUg4NTcuNzhjNy42NiwwLDEzLjg1LTUuOTEsMTMuODUtMTMuMjFTODY1LjQ0LDcyLjY4LDg1Ny43OCw3Mi42OFoiIGZpbGw9IiNlOGVlZWYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3R5bGU9ImZpbGw6IHZhcigtLWhwLWNvbG9yLTluYXo1Z0JWLTEpOyIvPjxwYXRoIGQ9Ik0xNTE0Ljc5LDc4Ni4yNlYxNjUuMTdDMTUxNC43OSw3My45NSwxNDM3LjI5LDAsMTM0MS43MiwwSDE4Ni45MkM5MS4zMywwLDEzLjg1LDczLjk1LDEzLjg1LDE2NS4xN1Y0NzUuNzFDNi4xOSw0NzUuNzEsMCw0ODEuNjIsMCw0ODguOTJWODU4LjkzYzAsNy4zLDYuMTksMTMuMjEsMTMuODUsMTMuMjF2MTMyLjE1QzYuMTksMTAwNC4yOSwwLDEwMTAuMiwwLDEwMTcuNXYxOTguMjFjMCw3LjMxLDYuMTksMTMuMjIsMTMuODUsMTMuMjJWMzA1OS4xYzAsOTEuMjMsNzcuNDgsMTY1LjE5LDE3My4wNywxNjUuMTloMTE1NC44Yzk1LjU3LDAsMTczLjA3LTczLjk2LDE3My4wNy0xNjUuMTlWMTAxNy41YzcuNjQsMCwxMy44NS01LjkxLDEzLjg1LTEzLjIxVjc5OS40N0MxNTI4LjY0LDc5Mi4xNywxNTIyLjQzLDc4Ni4yNiwxNTE0Ljc5LDc4Ni4yNlpNNjcwLjg2LDcyLjY4SDg1Ny43OGM3LjY2LDAsMTMuODUsNS45MSwxMy44NSwxMy4yMVM4NjUuNDQsOTkuMSw4NTcuNzgsOTkuMUg2NzAuODZjLTcuNjQsMC0xMy44NS01LjkxLTEzLjg1LTEzLjIxUzY2My4yMiw3Mi42OCw2NzAuODYsNzIuNjhabTY4OC40OCwzMDMyLjY3SDE2OS4zMmMtNjguODQsMC0xMjQuNjEtNTMuMjQtMTI0LjYxLTExOC45M1YyNjQuMjljMC02NS42OSw1NS43Ny0xMTguOTQsMTI0LjYxLTExOC45NEgxMzU5LjM0YzY4LjgxLDAsMTI0LjYxLDUzLjI1LDEyNC42MSwxMTguOTRsLjAxLDI3MjIuMTNDMTQ4My45NiwzMDUyLjExLDE0MjguMTYsMzEwNS4zNSwxMzU5LjM0LDMxMDUuMzVaIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0eWxlPSJmaWxsOiB2YXIoLS1ocC1jb2xvci05bmF6NWdCVi0yKTsiLz48L3N2Zz4=);" data-inner-natural-left="41.7" data-inner-natural-top="142.4" data-inner-natural-width="1445.2" data-inner-natural-height="2965.9"><iframe class="colorClassProxy"></iframe></div></div>

enter image description here enter image description here

vrintle
  • 5,501
  • 2
  • 16
  • 46
Crashalot
  • 33,605
  • 61
  • 269
  • 439
  • Could you include code for what you have tried so people can suggest you better on if you can improve the existing code or implement a new method? – TheUnKnown May 17 '19 at 19:59
  • @TheUnKnown just updated with code and a link to a codepen. thanks for your help! – Crashalot May 18 '19 at 01:42
  • 1
    Can we get some further feedback on the new comments and answers @Crashalot? For example: Ron Royston's answer (where you already responded) has a new comment that might add to the solution. If this still leads to problems we can search for new answers and approaches. – Aaron3219 May 19 '19 at 12:50
  • @Aaron3219 adding now, thanks for your time! – Crashalot May 19 '19 at 21:17
  • Edited my answer @Crashalot – Aaron3219 May 20 '19 at 18:14
  • You mean something like this? https://www.usecue.com/portfolio/studioinhout/ These are not even images, but actual websites... – Mr. Hugo May 23 '19 at 21:30

4 Answers4

4

Other answers

Before I provide my own approach with sources I will first discuss the answer from Ron Royston and show the possible problems / bumps.

Ron Royston's answer

You already pointed out the problem with this approach:

this doesn't work because if the frame has rounded corners like this one, the screenshot can still peek through depending on the background-size

A possible solution is provided by kthornbloom:

So add border-radius to the image element if it needs rounded corners, right?

Right! But this only works for some images. See, you can't apply a border-radius only to the background-image. Instead, you have to do it to the container itself.

Well, what is the problem in that? Just add a small enough border-radius so it doesn't cut the image, scale the background-image down and you are ready to go, right?
Not really! You have to decide what to do with your background-image's size. You can scale it down but then you can't use cover or something similar as the background-size. Unless you have an image that fits perfectly in the border of the smartphone image you will run into trouble. Also, the border-radius is obsolete if you scale your background-image down as it doesn't touch the borders anymore.

  .colorClassProxy {
  display: none
}

.itemBox,
.itemBox>* {
  position: absolute;
  box-sizing: border-box;
}

.itemBox {
  background-image: url('https://images.unsplash.com/photo-1500382017468-9049fed747ef?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80');
  border-radius: 15px;
  background-size: 95% 95%;
  background-repeat: no-repeat;
  background-position: center;
}

.backgroundColorBox,
.backgroundGraphicBox,
.foregroundBox,
.frameBox {
  width: 100%;
  height: 100%;
  background-size: contain;
  background-color: transparent;
  background-position: center;
  background-repeat: no-repeat;
  pointer-events: none;
<div class="itemBox graphic" id="NZW2Hmn4nVgb" style="width: 173px; height: 364px; top: 86px; left: 111px;">
  <div class="backgroundColorBox" style="width: 163px; height: 335px; top: 17px; left: 4px; -webkit-mask-image: url(&quot;data:image/svg+xml;charset=utf8,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%2241.7%20142.4%201445.2%202965.9%22%20preserveAspectRatio=%22none%22%3E%3Cpath%20d=%22M1359.3,3105.3h-1190c-68.8,0-124.6-53.2-124.6-118.9V264.3c0-65.7,55.8-118.9,124.6-118.9h1190%20%20c68.8,0,124.6,53.3,124.6,118.9l0,2722.1C1484,3052.1,1428.2,3105.4,1359.3,3105.3L1359.3,3105.3z%22%20style=%22fill:%20white;%20stroke:%20white;%20stroke-width:%206;%22/%3E%3C/svg%3E&quot;); background-size: cover;"></div>
  <div class="frameBox" data-natural-width="1528.64" data-natural-height="3224.29" style="background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTI4LjY0IiBoZWlnaHQ9IjMyMjQuMjkiIHZpZXdCb3g9IjAgMCAxNTI4LjY0IDMyMjQuMjkiIGRhdGEtaHAtY29sb3ItcHJlcGFyZWQ9InRydWUiIHN0eWxlPSItLWhwLWNvbG9yLTluYXo1Z0JWLTE6cmdiKDI1NSwgMjU1LCAyNTUpOyAtLWhwLWNvbG9yLTluYXo1Z0JWLTI6dXJsKFwjY29sb3ItOW5hejVnQlYtMik7Ij48cmFkaWFsR3JhZGllbnQgaWQ9ImNvbG9yLTluYXo1Z0JWLTIiIGRhdGEtY3NzPSJyYWRpYWwtZ3JhZGllbnQocmdiKDY3LCA2NywgNjcpLCByZ2IoMCwgMCwgMCkpIiBjeD0iNTAlIiBjeT0iNTAlIiByPSI3MC43MSUiPjxzdG9wIHN0b3AtY29sb3I9InJnYig2Nyw2Nyw2NykiIG9mZnNldD0iMCUiLz48c3RvcCBzdG9wLWNvbG9yPSJyZ2IoMCwwLDApIiBvZmZzZXQ9IjEwMCUiLz48L3JhZGlhbEdyYWRpZW50Pjx0aXRsZT5Bc3NldCAyPC90aXRsZT48cGF0aCBkPSJNODU3Ljc4LDcyLjY4SDY3MC44NmMtNy42NCwwLTEzLjg1LDUuOTEtMTMuODUsMTMuMjFzNi4yMSwxMy4yMSwxMy44NSwxMy4yMUg4NTcuNzhjNy42NiwwLDEzLjg1LTUuOTEsMTMuODUtMTMuMjFTODY1LjQ0LDcyLjY4LDg1Ny43OCw3Mi42OFoiIGZpbGw9IiNlOGVlZWYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3R5bGU9ImZpbGw6IHZhcigtLWhwLWNvbG9yLTluYXo1Z0JWLTEpOyIvPjxwYXRoIGQ9Ik0xNTE0Ljc5LDc4Ni4yNlYxNjUuMTdDMTUxNC43OSw3My45NSwxNDM3LjI5LDAsMTM0MS43MiwwSDE4Ni45MkM5MS4zMywwLDEzLjg1LDczLjk1LDEzLjg1LDE2NS4xN1Y0NzUuNzFDNi4xOSw0NzUuNzEsMCw0ODEuNjIsMCw0ODguOTJWODU4LjkzYzAsNy4zLDYuMTksMTMuMjEsMTMuODUsMTMuMjF2MTMyLjE1QzYuMTksMTAwNC4yOSwwLDEwMTAuMiwwLDEwMTcuNXYxOTguMjFjMCw3LjMxLDYuMTksMTMuMjIsMTMuODUsMTMuMjJWMzA1OS4xYzAsOTEuMjMsNzcuNDgsMTY1LjE5LDE3My4wNywxNjUuMTloMTE1NC44Yzk1LjU3LDAsMTczLjA3LTczLjk2LDE3My4wNy0xNjUuMTlWMTAxNy41YzcuNjQsMCwxMy44NS01LjkxLDEzLjg1LTEzLjIxVjc5OS40N0MxNTI4LjY0LDc5Mi4xNywxNTIyLjQzLDc4Ni4yNiwxNTE0Ljc5LDc4Ni4yNlpNNjcwLjg2LDcyLjY4SDg1Ny43OGM3LjY2LDAsMTMuODUsNS45MSwxMy44NSwxMy4yMVM4NjUuNDQsOTkuMSw4NTcuNzgsOTkuMUg2NzAuODZjLTcuNjQsMC0xMy44NS01LjkxLTEzLjg1LTEzLjIxUzY2My4yMiw3Mi42OCw2NzAuODYsNzIuNjhabTY4OC40OCwzMDMyLjY3SDE2OS4zMmMtNjguODQsMC0xMjQuNjEtNTMuMjQtMTI0LjYxLTExOC45M1YyNjQuMjljMC02NS42OSw1NS43Ny0xMTguOTQsMTI0LjYxLTExOC45NEgxMzU5LjM0YzY4LjgxLDAsMTI0LjYxLDUzLjI1LDEyNC42MSwxMTguOTRsLjAxLDI3MjIuMTNDMTQ4My45NiwzMDUyLjExLDE0MjguMTYsMzEwNS4zNSwxMzU5LjM0LDMxMDUuMzVaIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0eWxlPSJmaWxsOiB2YXIoLS1ocC1jb2xvci05bmF6NWdCVi0yKTsiLz48L3N2Zz4=);"
    data-inner-natural-left="41.7" data-inner-natural-top="142.4" data-inner-natural-width="1445.2" data-inner-natural-height="2965.9"><iframe class="colorClassProxy"></iframe></div>
</div>

Doesn't look good does it?
However, this approach does work in some cases. If the outline of the smartphone does not have any buttons on the side (like this one does) this approach will work. But I can't think of a smartphone that doesn't have buttons on the side.

Никита Гулис's answer (working)

Due to the missing markup this answer is very hard to read. However, it is mostly right and the best way to go most of the times. Actually, you have almost done it yourself with the clipping path. But first let's do the steps.

  1. Create an image and place it inside of the container.
  2. Scale it down to the right size.
  3. Position it the right way.
  4. Remove the clipping paths as it is unnecessary.

/*====Added this part====*/
.display-image {
  width: 165px;
  height: 340px;
  display: block;
  background-image: url('https://images.unsplash.com/photo-1500382017468-9049fed747ef?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80"');
  background-size: cover;
  margin-top: 15px;
  margin-left: 5px;
  border-radius: 10px;
}
/*=======================*/


.colorClassProxy {
  display: none
}

.itemBox,
.itemBox>* {
  position: absolute;
  box-sizing: border-box;
}

.backgroundColorBox,
.backgroundGraphicBox,
.foregroundBox,
.frameBox {
  width: 100%;
  height: 100%;
  background-size: contain;
  background-color: transparent;
  background-position: center;
  background-repeat: no-repeat;
  pointer-events: none;
<div class="itemBox graphic" id="NZW2Hmn4nVgb" style="width: 173px; height: 364px; top: 86px; left: 111px;">

  <!--====Added this line====-->
  <div class="display-image" alt=""></div>
  <!--=======================-->
  
  <!--====Removed the clipping path====-->
  <div class="frameBox" data-natural-width="1528.64" data-natural-height="3224.29" style="background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTI4LjY0IiBoZWlnaHQ9IjMyMjQuMjkiIHZpZXdCb3g9IjAgMCAxNTI4LjY0IDMyMjQuMjkiIGRhdGEtaHAtY29sb3ItcHJlcGFyZWQ9InRydWUiIHN0eWxlPSItLWhwLWNvbG9yLTluYXo1Z0JWLTE6cmdiKDI1NSwgMjU1LCAyNTUpOyAtLWhwLWNvbG9yLTluYXo1Z0JWLTI6dXJsKFwjY29sb3ItOW5hejVnQlYtMik7Ij48cmFkaWFsR3JhZGllbnQgaWQ9ImNvbG9yLTluYXo1Z0JWLTIiIGRhdGEtY3NzPSJyYWRpYWwtZ3JhZGllbnQocmdiKDY3LCA2NywgNjcpLCByZ2IoMCwgMCwgMCkpIiBjeD0iNTAlIiBjeT0iNTAlIiByPSI3MC43MSUiPjxzdG9wIHN0b3AtY29sb3I9InJnYig2Nyw2Nyw2NykiIG9mZnNldD0iMCUiLz48c3RvcCBzdG9wLWNvbG9yPSJyZ2IoMCwwLDApIiBvZmZzZXQ9IjEwMCUiLz48L3JhZGlhbEdyYWRpZW50Pjx0aXRsZT5Bc3NldCAyPC90aXRsZT48cGF0aCBkPSJNODU3Ljc4LDcyLjY4SDY3MC44NmMtNy42NCwwLTEzLjg1LDUuOTEtMTMuODUsMTMuMjFzNi4yMSwxMy4yMSwxMy44NSwxMy4yMUg4NTcuNzhjNy42NiwwLDEzLjg1LTUuOTEsMTMuODUtMTMuMjFTODY1LjQ0LDcyLjY4LDg1Ny43OCw3Mi42OFoiIGZpbGw9IiNlOGVlZWYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3R5bGU9ImZpbGw6IHZhcigtLWhwLWNvbG9yLTluYXo1Z0JWLTEpOyIvPjxwYXRoIGQ9Ik0xNTE0Ljc5LDc4Ni4yNlYxNjUuMTdDMTUxNC43OSw3My45NSwxNDM3LjI5LDAsMTM0MS43MiwwSDE4Ni45MkM5MS4zMywwLDEzLjg1LDczLjk1LDEzLjg1LDE2NS4xN1Y0NzUuNzFDNi4xOSw0NzUuNzEsMCw0ODEuNjIsMCw0ODguOTJWODU4LjkzYzAsNy4zLDYuMTksMTMuMjEsMTMuODUsMTMuMjF2MTMyLjE1QzYuMTksMTAwNC4yOSwwLDEwMTAuMiwwLDEwMTcuNXYxOTguMjFjMCw3LjMxLDYuMTksMTMuMjIsMTMuODUsMTMuMjJWMzA1OS4xYzAsOTEuMjMsNzcuNDgsMTY1LjE5LDE3My4wNywxNjUuMTloMTE1NC44Yzk1LjU3LDAsMTczLjA3LTczLjk2LDE3My4wNy0xNjUuMTlWMTAxNy41YzcuNjQsMCwxMy44NS01LjkxLDEzLjg1LTEzLjIxVjc5OS40N0MxNTI4LjY0LDc5Mi4xNywxNTIyLjQzLDc4Ni4yNiwxNTE0Ljc5LDc4Ni4yNlpNNjcwLjg2LDcyLjY4SDg1Ny43OGM3LjY2LDAsMTMuODUsNS45MSwxMy44NSwxMy4yMVM4NjUuNDQsOTkuMSw4NTcuNzgsOTkuMUg2NzAuODZjLTcuNjQsMC0xMy44NS01LjkxLTEzLjg1LTEzLjIxUzY2My4yMiw3Mi42OCw2NzAuODYsNzIuNjhabTY4OC40OCwzMDMyLjY3SDE2OS4zMmMtNjguODQsMC0xMjQuNjEtNTMuMjQtMTI0LjYxLTExOC45M1YyNjQuMjljMC02NS42OSw1NS43Ny0xMTguOTQsMTI0LjYxLTExOC45NEgxMzU5LjM0YzY4LjgxLDAsMTI0LjYxLDUzLjI1LDEyNC42MSwxMTguOTRsLjAxLDI3MjIuMTNDMTQ4My45NiwzMDUyLjExLDE0MjguMTYsMzEwNS4zNSwxMzU5LjM0LDMxMDUuMzVaIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0eWxlPSJmaWxsOiB2YXIoLS1ocC1jb2xvci05bmF6NWdCVi0yKTsiLz48L3N2Zz4=);"
    data-inner-natural-left="41.7" data-inner-natural-top="142.4" data-inner-natural-width="1445.2" data-inner-natural-height="2965.9"><iframe class="colorClassProxy"></iframe></div>
</div>

Short note: The reference he provided does not actually represent what he wrote.

I took a look at the reason why you opened the bounty:

Looking for an answer drawing from credible and/or official sources.

You can see it on every website that uses an image as the smartphone.

  • It has been discussed on stack overflow once right here.
  • I created a test app with an "website to app" converter. On this website you can test the app out and they used the same approach. The only difference is that they can't have an image as the display. Instead, they use a canvas. Sadly I can't implement it in my answer so I have to post the link instead.
  • The same can be seen on this site.

The examples I provided are all rather big companies and if you search for more exmaples you will find more of them. In my opinion these examples make this approach credible.

Edit

Based on the comments I will complete the answer.
This depends on how much you can edit your picture. I assume that you own the pictures or are allowed to use them. Here is one approach:

First of all I thought I can just position it and scale the image. Turns out that the smartphone in the image is slightly turned. So one approach would be to use CSS for this. You will have to work with skew() (CSS), but this is very, very, very hard to nail. Also, if you just put it on the image you will run into the same problem as you had before with your clipping paths. So this does not seem to be the solution.

Another approach is the following:
It should be no problem for you to just convert it to a PNG (Portable Network Graphic) and cut out the white display to make it transparent.

  1. Give the image you want in the display a lower z-index.
  2. Position it and scale it.

However, this is only approximatly. You can of course just skew and transform the image until no one would notice. But this doesn't seem to be the "clearest" way does it?

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.display-image {
  width: 184px;
  height: 322px;
  display: block;
  background-image: url('https://images.unsplash.com/photo-1500382017468-9049fed747ef?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80"');
  background-size: cover;
  top: 165px;
  left: 219px;
  position: absolute;
  transform: rotate(0.2deg);
  z-index: -1;
}

.itemBox {
  position: relative;
  box-sizing: border-box;
}

.frameBox {
  z-index: 1;
}
<div class="itemBox graphic" id="NZW2Hmn4nVgb" style="width: 173px; height: 364px; top: 86px; left: 111px;">

  <!--====Added this line====-->
  <div class="display-image" alt=""></div>
  <!--=======================-->

  <!--====Removed the clipping path====-->
  <div class="frameBox"><img alt="" src="https://i.imgur.com/jbHkPuf.png"></div>
</div>

The only clean way I can think of is using canvas. As you can see here it is possible to distord images with it to your liking. You will have to draw the whole "smartphone inside hand" picture in the canvas and then draw the image that should be on the display on the needed coordinates. But you have to decide if it is worth the time because it is fairly time consuming and complex and a lot of code. I decided it isn't and that's why there is no working exmaple. Also, you can't just use things like background-size: cover so you will have to cut the image to the smartphones size if you don't want to write a huge junk of JS. In my opinion the approach before this is the best approach.

Of course I could have overlooked something so feel free to correct me or edit my answer.

Community
  • 1
  • 1
Aaron3219
  • 2,168
  • 4
  • 11
  • 28
  • 2
    To be clear: It is totally fine if I don't get any credit for this answer. However, I think I delivered a valuable analysis, corrected the (almost) correct answer and extended it with a working example and provided sources. In my opinion my answer is valuable. – Aaron3219 May 19 '19 at 15:04
  • 1
    Well an answer can't be right if you can't read it / understand it. Also, (as you said) it only is almost correct. For me this is the correct answer! –  May 19 '19 at 15:06
  • Thanks for your comprehensive and thoughtful answer! How would this work for more complicated cases where the frame isn't the only object. Ex: https://www.freepik.com/free-photo/hand-holding-smartphone-with-blank-screen_987726.htm – Crashalot May 19 '19 at 21:30
  • Will start editing in a second and notify you @Crashalot – Aaron3219 May 19 '19 at 21:30
2

You can create a div behind the frame, scale it just a little bit down so the border would be inside of the border of the phone. Then round the corners of the div and give it overflow: hidden; Then place an image inside, give it height and width of 100% and object-fit:cover; Heres one of my websites with similar effect so you can have a reference. reference (phones are visible on medium to big screens)

  • how would this work for more complicated cases where the frame isn't the only object. example: https://www.freepik.com/free-photo/hand-holding-smartphone-with-blank-screen_987726.htm – Crashalot May 19 '19 at 21:25
  • @Crashalot As long as you know proportions of the frame (in this case hand and a phone) there should be no problem. You can 1) absolutely position your div with image behind a frame. 2) Then give it a width and a height in percents according to the size of the phone (and also maybe with the help of calc() to calculate padding from the border of the phone) . 3) Then finally you can round the corners if needed and give your image object-fit: cover; so it would fit nicely. – Никита Гулис May 19 '19 at 21:31
  • This will not work. The image is more complex because the smartphone is held at an angle. I completed my answer. – Aaron3219 May 19 '19 at 22:45
  • @Aaron3219 So if an image is at an angle i would position div with an image according to one of the corners of the phone. Then make that corner of a div it's transform-origin. Then rotate the div to a necessary degree. – Никита Гулис May 19 '19 at 22:48
  • Won't work either because the phone is not only rotated at the x-axis but also on the z-axis. – Aaron3219 May 19 '19 at 22:50
  • @Aaron3219 We can add an additional container for the image for every axis so we could rotate it in every direction with different transform-origins. – Никита Гулис May 19 '19 at 22:52
  • @НикитаГулис I don't understand what you are trying to do. Can you provide a minimal exmaple? – Aaron3219 May 19 '19 at 22:54
  • @НикитаГулис Ah! I understand. Yes this would be working. If it is worth the time this is a possible solution. But using this method is the same as using `skew`. The only difference is that you have different containers on different axes. I thought about using `rotate3d()` or `matrix()`. Downside to this is that you will have to work with vectors... since we don't know the degree the phone is held we have to try and try until it fits. – Aaron3219 May 19 '19 at 23:06
2

I think the easiest way is to apply the image as a CSS background-image then use background-size's contain or cover.

Scaling background images

The background-size CSS property makes it possible to adjust the width and height of background images, thus overriding the default behavior which tiles background images at their full size. You can scale the image upward or downward as desired.

Ronnie Royston
  • 16,778
  • 6
  • 77
  • 91
  • this doesn't work because if the frame has rounded corners like this one, the screenshot can still peek through depending on the background-size as illustrated in the second image. we don't want the screenshot to appear beyond the frame. – Crashalot May 17 '19 at 22:55
  • 2
    So add border-radius to the image element if it needs rounded corners, right? – kthornbloom May 18 '19 at 01:48
2

The simplest solution

Using a phone with a black screen and a clipping mask. There will be no visible gaps and you will get very clean code. You can use background-size: cover or 100% 100% to either 'cover' or 'stretch fit' the image.

So to be clear... the layers will look like this:

  • Layer 1 has a background-image showing the phone. The phone has a solid black area where the screenshot should be (the screen). This layer is shown in the image below with a blue border.
  • Layer 2 has a background-image showing the screenshot. This screenshot is clipped by a clipping mask in the exact shape of the screen (an SVG), like this. This layer is shown in the image below with a red border.

Layer 1 - Phone with black screen

enter image description here

Here you see the phone/image with a black screen and a transparent background.

Layer 2 - Screenshot image with clipping mask

enter image description here

Note that the transparenty is caused by the clipping mask and/or the border radius. The screenshot itsself is not transparent (just a simple rectangular JPG). Also note that you should trim the white space in the SVG to make it easier to position this layer.

The code

Use this simple HTML:

<div class='layer 1'>
  <div class='layer2'></div>
</div>

<svg width="0" height="0">
  <defs>
    <clipPath id="myClip">
      ... [add the complex clip path definition here]
    </clipPath>
  </defs>
</svg>

And some CSS:

.layer1 {
  position: relative;  
  width: 500px; 
  padding-bottom: 150%;
  background: url(phone.png) center center / contain no-repeat;
  /*border: 1px solid blue;*/
}
.layer2 {
  position: absolute;
  width: 470px;
  top: 40px;
  left: 15px;
  padding-bottom: 150%;
  background: url(screenshot.jpg) center center / 100% 100% no-repeat;
  border-radius: 30px;
  clip-path: url(#myClip);
  /*border: 1px solid red;*/
}

Change the 100% 100% to cover and or adjust the percentage. When you change it to cover you should make sure you trimmed all white-space from this layer, so you are left with the rectangle in the red line. Note that any rotations or transform should be added to the outer element (layer 1).

Browser support

People seem to worry about outdated non-standard-compliant browsers. I get that, but I used an inline SVG, which is currently supported in all major browsers. Only IE11, Edge18, Opera Mini, Blackberry and IE Mobile do not support this. If the browser does not support clip-path with inline SVG, you will get the same result BUT WITHOUT THE NOTCH. Seems reasonable to me, as this problem gets smaller every day.

Mr. Hugo
  • 11,887
  • 3
  • 42
  • 60
  • 2
    Downside to this is that `clip-path` [lacks browser support](https://caniuse.com/#search=clip-path). Edge for example does not support it at all if you don't enable it manually. Also, most other common used browsers are at least limited to some extend. – Aaron3219 May 24 '19 at 11:36
  • It even says so in the link you provided ([click](https://css-tricks.com/clipping-masking-css/)): "It's so hard so summarize succinctly, since different properties and even values have different support levels all over the place. Not to mention how you use them and on what." –  May 24 '19 at 11:42
  • I think this solution shows a notch if clip-path is supported and shows no notch if it is not supported. If it is not supported the border-radius takes over and leaves you with a pretty decent result. I added a red line to indicate the edges of the screenshot/layer2 to make extra clear how it works. – Mr. Hugo May 24 '19 at 12:15
  • @Aaron To be more precise... you will get a missing notch in IE11, Edge18, Opera Mini, Blackberry and IE Mobile. I could live with that. – Mr. Hugo May 24 '19 at 12:19
  • 1
    @JoostS "the border-radius takes over" if the display has the same curvature! But otherwise (if you can renounce on the notch) this is a good approach. Good job. – Aaron3219 May 24 '19 at 15:37