• Effects
    • Scroll Effects
    • Text Effects
    • Shadow
  • Essentials
    • Arrows
    • Buttons
    • Background Patterns
    • Border Examples
    • Cards
    • Color Palettes
    • Dividers
    • Link styles
    • Loaders
    • Modal Windows
    • Notifications
    • Progress bar
    • Quote styles
    • Spinner
    • Tooltips
  • Media
    • Calendars
    • Carousels
    • Clocks
    • Gallery
    • Music Players
    • Sliders
    • Slideshows
    • Tables
    • Thumbnails
  • Navigation
  • Inputs
    • Range Sliders
    • Checkboxes
    • Toggle Switches
  • Script
    • Angularjs
    • Backbone.js
    • bootstrap
    • jQuery
    • ReactJs
    • JavaScript
    • Syntax Highlighters
    • tryit editor
    • PHP
  • API’s
    • Facebook
    • Google
    • Indeed
    • Twitter
    • YouTube
  • Tools
w3tweaks.com
  • Effects
    • Scroll Effects
    • Text Effects
    • Shadow
  • Essentials
    • Arrows
    • Buttons
    • Background Patterns
    • Border Examples
    • Cards
    • Color Palettes
    • Dividers
    • Link styles
    • Loaders
    • Modal Windows
    • Notifications
    • Progress bar
    • Quote styles
    • Spinner
    • Tooltips
  • Media
    • Calendars
    • Carousels
    • Clocks
    • Gallery
    • Music Players
    • Sliders
    • Slideshows
    • Tables
    • Thumbnails
  • Navigation
  • Inputs
    • Range Sliders
    • Checkboxes
    • Toggle Switches
  • Script
    • Angularjs
    • Backbone.js
    • bootstrap
    • jQuery
    • ReactJs
    • JavaScript
    • Syntax Highlighters
    • tryit editor
    • PHP
  • API’s
    • Facebook
    • Google
    • Indeed
    • Twitter
    • YouTube
  • Tools
w3tweaks.com
Home Media

Realistic 3D Photo Card Gallery

November 27, 2019
in Media

Cool 3d photo gallery developed using HTML (Pug), CSS (Scss), JS (Babel) and Vue.Js. To see the effect, mouse hover any image and see the 3d effects when pointer is zig zagged.

Developed by Jouan Marcel and 3D photo gallery in a semi skeuomorphism style with realistic lighting reflection on mouse / cursor hover. Created with Vue.Js as web component for portability.

You might also like

Animated Fullscreen and grid view Slider

Unicycle Range Slider

15 CSS Sliders

10 CSS Logo Designs

CSS Advent Calendar

HTML Audio Player

Find the demo below

See the Pen Realistic 3D Photo Cards (Hover Effect, Vue.Js) by Jouan Marcel (@jouanmarcel) on CodePen.

Find the code below

HTML
Pug View Compiled HTML Pug
.grid#grid
  photo-card(img="https://scontent-dus1-1.cdninstagram.com/vp/37f408a5a9bfcb4b0e2de07499a560c9/5E632BE6/t51.2885-15/e35/c0.135.1080.1080a/s480x480/40552940_106792203550584_1954235443271646170_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&_nc_cat=100" link="https://www.instagram.com/p/BnrVNA_F95p/")
  photo-card(img="https://scontent-dus1-1.cdninstagram.com/vp/398cfc6bf836e3e227ee922936836bbe/5E4D6E72/t51.2885-15/e35/s480x480/36955358_287866911973922_429811318774562816_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&_nc_cat=102" link="https://www.instagram.com/p/BloI_FblUVy/")
  photo-card(img="https://scontent-dus1-1.cdninstagram.com/vp/889f95936f24056538c7168915d639c3/5E51E9AC/t51.2885-15/e35/c135.0.809.809a/s480x480/41557300_447533602406412_6139564580899339847_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&_nc_cat=100" link="https://www.instagram.com/p/BoKSUPGg5al/")
  photo-card(img="https://scontent-dus1-1.cdninstagram.com/vp/ec6d1a1d6f459aba44c4a8cfd462f1ad/5E64975E/t51.2885-15/sh0.08/e35/c89.0.902.902a/s640x640/69834747_986466475037071_2879916583938753044_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&_nc_cat=107" link="https://www.instagram.com/p/B1rXN_roTv4/")
  photo-card(img="https://scontent-dus1-1.cdninstagram.com/vp/9212c0823c2e2c82431843d44890db22/5E3EAC0D/t51.2885-15/e35/s480x480/47585211_2233880496884813_4296136872377091209_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&_nc_cat=105" link="https://www.instagram.com/p/BsdpIh6BxPB/")
  photo-card(img="https://scontent-dus1-1.cdninstagram.com/vp/cc45be94aa39443f8f485e149e420718/5E5CC0A1/t51.2885-15/sh0.08/e35/c135.0.809.809a/s640x640/39956004_317471385681570_6819068332604391424_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&_nc_cat=106" link="https://www.instagram.com/p/BnT_d4XFuJJ/")
  photo-card(img="https://scontent-dus1-1.cdninstagram.com/vp/dd0a7fe266706839b5ddd0fe4142c9c0/5E406CFA/t51.2885-15/sh0.08/e35/c135.0.809.809a/s640x640/37684559_242343189922222_578856764234006528_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&_nc_cat=110" link="https://www.instagram.com/p/Bl8mOXKlp7N/")
  photo-card(img="https://scontent-dus1-1.cdninstagram.com/vp/ad1d8f6eca2a3116c6da1aad08406ce8/5E449EC6/t51.2885-15/sh0.08/e35/c135.0.809.809a/s640x640/37189542_2061235184195776_8978574457354321920_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&_nc_cat=104" link="https://www.instagram.com/p/BlYb96KlH99/")
  
View Compiled HTML
<div class="grid" id="grid">
  <photo-card img="https://scontent-dus1-1.cdninstagram.com/vp/37f408a5a9bfcb4b0e2de07499a560c9/5E632BE6/t51.2885-15/e35/c0.135.1080.1080a/s480x480/40552940_106792203550584_1954235443271646170_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&amp;_nc_cat=100" link="https://www.instagram.com/p/BnrVNA_F95p/"></photo-card>
  <photo-card img="https://scontent-dus1-1.cdninstagram.com/vp/398cfc6bf836e3e227ee922936836bbe/5E4D6E72/t51.2885-15/e35/s480x480/36955358_287866911973922_429811318774562816_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&amp;_nc_cat=102" link="https://www.instagram.com/p/BloI_FblUVy/"></photo-card>
  <photo-card img="https://scontent-dus1-1.cdninstagram.com/vp/889f95936f24056538c7168915d639c3/5E51E9AC/t51.2885-15/e35/c135.0.809.809a/s480x480/41557300_447533602406412_6139564580899339847_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&amp;_nc_cat=100" link="https://www.instagram.com/p/BoKSUPGg5al/"></photo-card>
  <photo-card img="https://scontent-dus1-1.cdninstagram.com/vp/ec6d1a1d6f459aba44c4a8cfd462f1ad/5E64975E/t51.2885-15/sh0.08/e35/c89.0.902.902a/s640x640/69834747_986466475037071_2879916583938753044_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&amp;_nc_cat=107" link="https://www.instagram.com/p/B1rXN_roTv4/"></photo-card>
  <photo-card img="https://scontent-dus1-1.cdninstagram.com/vp/9212c0823c2e2c82431843d44890db22/5E3EAC0D/t51.2885-15/e35/s480x480/47585211_2233880496884813_4296136872377091209_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&amp;_nc_cat=105" link="https://www.instagram.com/p/BsdpIh6BxPB/"></photo-card>
  <photo-card img="https://scontent-dus1-1.cdninstagram.com/vp/cc45be94aa39443f8f485e149e420718/5E5CC0A1/t51.2885-15/sh0.08/e35/c135.0.809.809a/s640x640/39956004_317471385681570_6819068332604391424_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&amp;_nc_cat=106" link="https://www.instagram.com/p/BnT_d4XFuJJ/"></photo-card>
  <photo-card img="https://scontent-dus1-1.cdninstagram.com/vp/dd0a7fe266706839b5ddd0fe4142c9c0/5E406CFA/t51.2885-15/sh0.08/e35/c135.0.809.809a/s640x640/37684559_242343189922222_578856764234006528_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&amp;_nc_cat=110" link="https://www.instagram.com/p/Bl8mOXKlp7N/"></photo-card>
  <photo-card img="https://scontent-dus1-1.cdninstagram.com/vp/ad1d8f6eca2a3116c6da1aad08406ce8/5E449EC6/t51.2885-15/sh0.08/e35/c135.0.809.809a/s640x640/37189542_2061235184195776_8978574457354321920_n.jpg?_nc_ht=scontent-dus1-1.cdninstagram.com&amp;_nc_cat=104" link="https://www.instagram.com/p/BlYb96KlH99/"></photo-card>
</div>
CSS
Sass View Compiled CSS Sass
body
  margin: 0
  min-height: 100vh
  display: flex
  flex-direction: column
  align-items: center
  justify-content: center
  background-image: radial-gradient(circle, #fff 30%, #ccc)
  padding: 0 40px
  font-family: "Source Sans Pro", Helvetica, sans-serif
  font-weight: 300
#grid
  display: grid
  grid-template-columns: repeat(auto-fill, 150px)
  grid-column-gap: 30px
  grid-row-gap: 30px
  align-items: center
  justify-content: center
  width: 100%
  max-width: 700px
  .card
    background-color: #ccc
    width: 150px
    height: 150px
    transition: all .1s ease
    border-radius: 3px
    position: relative
    z-index: 1
    box-shadow: 0 0 5px rgba(0, 0, 0, 0)
    overflow: hidden
    cursor: pointer
    &:hover
      transform: scale(2)
      z-index: 2
      box-shadow: 0 10px 20px rgba(0, 0, 0, .4)
      img
        filter: grayscale(0)
    .reflection
      position: absolute
      width: 100%
      height: 100%
      z-index: 2
      left: 0
      top: 0
      transition: all .1s ease
      opacity: 0
      mix-blend-mode: soft-light
    img
      width: 100%
      height: 100%
      object-fit: cover
      filter: grayscale(.65)
      transition: all .3s ease
View Compiled CSS
body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-image: radial-gradient(circle, #fff 30%, #ccc);
  padding: 0 40px;
  font-family: "Source Sans Pro", Helvetica, sans-serif;
  font-weight: 300;
}
#grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, 150px);
  grid-column-gap: 30px;
  grid-row-gap: 30px;
  align-items: center;
  justify-content: center;
  width: 100%;
  max-width: 700px;
}
#grid .card {
  background-color: #ccc;
  width: 150px;
  height: 150px;
  transition: all 0.1s ease;
  border-radius: 3px;
  position: relative;
  z-index: 1;
  box-shadow: 0 0 5px rgba(0, 0, 0, 0);
  overflow: hidden;
  cursor: pointer;
}
#grid .card:hover {
  -webkit-transform: scale(2);
          transform: scale(2);
  z-index: 2;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.4);
}
#grid .card:hover img {
  -webkit-filter: grayscale(0);
          filter: grayscale(0);
}
#grid .card .reflection {
  position: absolute;
  width: 100%;
  height: 100%;
  z-index: 2;
  left: 0;
  top: 0;
  transition: all 0.1s ease;
  opacity: 0;
  mix-blend-mode: soft-light;
}
#grid .card img {
  width: 100%;
  height: 100%;
  -o-object-fit: cover;
     object-fit: cover;
  -webkit-filter: grayscale(0.65);
          filter: grayscale(0.65);
  transition: all 0.3s ease;
}
JavaScript
Babel View Compiled JS Babel
Vue.component("photo-card", {
  template: `<a class="card"
                :href="link"
                target="_blank"
                ref="card"
                @mousemove="move"
                @mouseleave="leave"
                @mouseover="over">
                  <div class="reflection" ref="refl"></div>
                  <img :src="img"/>
            </a>`,
  props: ["img", "link"],
  mounted() {},
  data: () => ({
    debounce: null,
  }),
  methods: {
    over() {
      const refl = this.$refs.refl;
      refl.style.opacity = 1;
    },
    leave() {
      const card = this.$refs.card;
      const refl = this.$refs.refl;
      card.style.transform = `perspective(500px) scale(1)`;
      refl.style.opacity = 0;
    },
    move() {
      const card = this.$refs.card;
      const refl = this.$refs.refl;
      const relX = (event.offsetX + 1) / card.offsetWidth;
      const relY = (event.offsetY + 1) / card.offsetHeight;
      const rotY = `rotateY(${(relX - 0.5) * 60}deg)`;
      const rotX = `rotateX(${(relY - 0.5) * -60}deg)`;
      card.style.transform = `perspective(500px) scale(2) ${rotY} ${rotX}`;
      const lightX = this.scale(relX, 0, 1, 150, -50);
      const lightY = this.scale(relY, 0, 1, 30, -100);
      const lightConstrain = Math.min(Math.max(relY, 0.3), 0.7);
      const lightOpacity = this.scale(lightConstrain, 0.3, 1, 1, 0) * 255;
      const lightShade = `rgba(${lightOpacity}, ${lightOpacity}, ${lightOpacity}, 1)`;
      const lightShadeBlack = `rgba(0, 0, 0, 1)`;
      refl.style.backgroundImage = `radial-gradient(circle at ${lightX}% ${lightY}%, ${lightShade} 20%, ${lightShadeBlack})`;
    },
    scale: (val, inMin, inMax, outMin, outMax) =>
      outMin + (val - inMin) * (outMax - outMin) / (inMax - inMin)
  }
});
const app = new Vue({
  el: "#grid"
});
View Compiled JS
Vue.component("photo-card", {
  template: `<a class="card"
                :href="link"
                target="_blank"
                ref="card"
                @mousemove="move"
                @mouseleave="leave"
                @mouseover="over">
                  <div class="reflection" ref="refl"></div>
                  <img :src="img"/>
            </a>`,
  props: ["img", "link"],
  mounted() {},
  data: () => ({
    debounce: null }),
  methods: {
    over() {
      const refl = this.$refs.refl;
      refl.style.opacity = 1;
    },
    leave() {
      const card = this.$refs.card;
      const refl = this.$refs.refl;
      card.style.transform = `perspective(500px) scale(1)`;
      refl.style.opacity = 0;
    },
    move() {
      const card = this.$refs.card;
      const refl = this.$refs.refl;
      const relX = (event.offsetX + 1) / card.offsetWidth;
      const relY = (event.offsetY + 1) / card.offsetHeight;
      const rotY = `rotateY(${(relX - 0.5) * 60}deg)`;
      const rotX = `rotateX(${(relY - 0.5) * -60}deg)`;
      card.style.transform = `perspective(500px) scale(2) ${rotY} ${rotX}`;
      const lightX = this.scale(relX, 0, 1, 150, -50);
      const lightY = this.scale(relY, 0, 1, 30, -100);
      const lightConstrain = Math.min(Math.max(relY, 0.3), 0.7);
      const lightOpacity = this.scale(lightConstrain, 0.3, 1, 1, 0) * 255;
      const lightShade = `rgba(${lightOpacity}, ${lightOpacity}, ${lightOpacity}, 1)`;
      const lightShadeBlack = `rgba(0, 0, 0, 1)`;
      refl.style.backgroundImage = `radial-gradient(circle at ${lightX}% ${lightY}%, ${lightShade} 20%, ${lightShadeBlack})`;
    },
    scale: (val, inMin, inMax, outMin, outMax) =>
    outMin + (val - inMin) * (outMax - outMin) / (inMax - inMin) } });
const app = new Vue({
  el: "#grid" });
Tags: 3d gallerygallery
Previous Post

3d Text effect using only css3

Next Post

CSS Split text with shadow

Related Stories

Animated Fullscreen and grid view Slider
Media

Animated Fullscreen and grid view Slider

July 8, 2020
Unicycle Range Slider
Media

Unicycle Range Slider

July 7, 2020
CSS Sliders
Media

15 CSS Sliders

June 27, 2020
10 CSS Logo Designs
Media

10 CSS Logo Designs

May 11, 2020
CSS Advent Calendar
Media

CSS Advent Calendar

December 24, 2019
HTML Audio Player
Media

HTML Audio Player

December 10, 2019
Next Post
split text with shadow using css

CSS Split text with shadow

You might also like

CSS Cards

100 Creative CSS Cards

November 13, 2022
Multi step html form

44 Free Multi step HTML forms

March 7, 2023
CSS & HTML Dashboard Templates

13 Free HTML & CSS Dashboard Template Designs

December 29, 2021
49 CSS Tables

49 CSS Tables

November 13, 2019
HTML & CSS pricing tables

20 HTML & CSS pricing tables

May 2, 2020
CSS Dark Mode

14 Best CSS Dark Mode

October 13, 2021
w3tweaks

Unleash your front-end development potential by exploring the ultimate collection of UI designs and patterns, and play with them to create stunning websites through our tutorials.

Tags

Angularjs AngularJS Tutorials animation animation examples Button button hover effect Buttons Calendar calendars cards click buttons CSS css3 css buttons css calendar css calendars css effects css hover effects demo effect effects essentials forms free Free Tool hover hover animation Hover effects html inputs Javascript jquery js learn loaders menu mouse hover effects navigation php script text effects tool tutorial tutorials YouTube

Stay Connected

  • Effects
    • Scroll Effects
    • Text Effects
    • Shadow
  • Essentials
    • Arrows
    • Buttons
    • Background Patterns
    • Border Examples
    • Cards
    • Color Palettes
    • Dividers
    • Link styles
    • Loaders
    • Modal Windows
    • Notifications
    • Progress bar
    • Quote styles
    • Spinner
    • Tooltips
  • Media
    • Calendars
    • Carousels
    • Clocks
    • Gallery
    • Music Players
    • Sliders
    • Slideshows
    • Tables
    • Thumbnails
  • Navigation
  • Inputs
    • Range Sliders
    • Checkboxes
    • Toggle Switches
  • Script
    • Angularjs
    • Backbone.js
    • bootstrap
    • jQuery
    • ReactJs
    • JavaScript
    • Syntax Highlighters
    • tryit editor
    • PHP
  • API’s
    • Facebook
    • Google
    • Indeed
    • Twitter
    • YouTube
  • Tools

Welcome Back!

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In
x
x