JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<body>
<div class="container">
<h1>Image Zoom</h1>
<p>
Trust fund seitan chia, wolf lomo letterpress Bushwick before they sold out. Carles kogi fixie, squid twee Tonx readymade cred typewriter scenester locavore kale chips vegan organic. Meggings pug wolf Shoreditch typewriter skateboard. McSweeney's iPhone
chillwave, food truck direct trade disrupt flannel irony tousled Cosby sweater single-origin coffee. Organic disrupt bicycle rights, tattooed messenger bag flannel craft beer fashion axe bitters. Readymade sartorial craft beer, quinoa sustainable
butcher Marfa Echo Park Terry Richardson gluten-free flannel retro cred mlkshk banjo. Salvia 90's art party Blue Bottle, PBR&B cardigan 8-bit.
</p>
<p>
Meggings irony fashion axe, tattooed master cleanse Blue Bottle stumptown bitters authentic flannel freegan paleo letterpress ugh sriracha. Wolf PBR&B art party aesthetic meh cliche. Sartorial before they sold out deep v, aesthetic PBR&B craft beer post-ironic
synth keytar pork belly skateboard pour-over. Tonx cray pug Etsy, gastropub ennui wolf ethnic Odd Future viral master cleanse skateboard banjo. Pitchfork scenester cornhole, whatever try-hard ethnic banjo +1 gastropub American Apparel vinyl skateboard
Shoreditch seitan. Blue Bottle keffiyeh Austin Helvetica art party. Portland ethnic fixie, beard retro direct trade ugh scenester Tumblr readymade authentic plaid pickled hashtag biodiesel.
</p>
<img src="https://fat.github.io/zoom.js/img/palm.jpg" data-original="https://www.rodiaki.gr/medias/2017/02/28/dd219ad13d56b149e2d5237b87a78350.jpg?tt=81230be5ceef02a94c0308fe59987a4e" data-action="zoom">
<p>
Thundercats freegan Truffaut, four loko twee Austin scenester lo-fi seitan High Life paleo quinoa cray. Schlitz butcher...
CSS
/** zoom.css **/
img[data-action="zoom"] {
cursor: pointer;
cursor: -webkit-zoom-in;
cursor: -moz-zoom-in;
}
.zoom-img,
.zoom-img-wrap {
position: relative;
z-index: 666;
-webkit-transition: all 300ms;
-o-transition: all 300ms;
transition: all 300ms;
}
img.zoom-img {
cursor: pointer;
cursor: -webkit-zoom-out;
cursor: -moz-zoom-out;
}
.zoom-overlay {
z-index: 420;
background: #fff;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
filter: "alpha(opacity=0)";
opacity: 0;
-webkit-transition: opacity 300ms;
-o-transition: opacity 300ms;
transition: opacity 300ms;
}
.zoom-overlay-open .zoom-overlay {
filter: "alpha(opacity=100)";
opacity: 1;
}
.zoom-overlay-open,
.zoom-overlay-transitioning {
cursor: default;
}
/****** end zoom.css *****/
/* SIMPLE DEMO STYLES */
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 12px;
line-height: 1.6;
}
.container {
margin: 50px;
max-width: 700px;
}
.container img {
width: 100%;
}
.container .pull-left {
width: 55%;
float: left;
margin: 20px 20px 20px -80px;
}
@media (min-width: 750px) {
body {
font-size: 16px;
line-height: 1.6;
}
.container {
margin: 100px auto;
}
}
JavaScript
;
(function($) {
"use strict";
/**
* The zoom service
*/
function ZoomService() {
this._activeZoom =
this._initialScrollPosition =
this._initialTouchPosition =
this._touchMoveListener = null
this._$document = $(document)
this._$window = $(window)
this._$body = $(document.body)
this._boundClick = $.proxy(this._clickHandler, this)
}
ZoomService.prototype.listen = function() {
this._$body.on('click', '[data-action="zoom"]', $.proxy(this._zoom, this))
}
ZoomService.prototype._zoom = function(e) {
var target = e.target
if (!target || target.tagName != 'IMG') return
if (this._$body.hasClass('zoom-overlay-open')) return
if (e.metaKey || e.ctrlKey) {
return window.open((e.target.getAttribute('data-original') || e.target.src), '_blank')
}
if (target.width >= ($(window).width() - Zoom.OFFSET)) return
this._activeZoomClose(true)
this._activeZoom = new Zoom(target)
this._activeZoom.zoomImage()
// todo(fat): probably worth throttling this
this._$window.on('scroll.zoom', $.proxy(this._scrollHandler, this))
this._$document.on('keyup.zoom', $.proxy(this._keyHandler, this))
this._$document.on('touchstart.zoom', $.proxy(this._touchStart, this))
// we use a capturing phase here to prevent unintended js events
// sadly no useCapture in jquery api (http://bugs.jquery.com/ticket/14953)
if (document.addEventListener) {
document.addEventListener('click', this._boundClick, true)
} else {
document.attachEvent('onclick', this._boundClick, true)
}
if ('bubbles' in e) {
if (e.bubbles) e.stopPropagation()
} else {
// Internet Explorer before version 9
e.cancelBubble = true
}
}
ZoomService.prototype._activeZoomClose = function(forceDispose) {
if (!this._activeZoom) return
if (forceDispose) {
this._activeZoom.dispose()
} else {
this._activeZoom.close()
}
...