Skip to content

Commit

Permalink
Merge pull request #8 from laurenashpole/new-features
Browse files Browse the repository at this point in the history
New props and features
  • Loading branch information
laurenashpole authored May 13, 2021
2 parents 7b9faeb + 65d7327 commit 9b4ec75
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 55 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,20 @@ src | String | | (Required) URL for the original image.
srcSet | String | | Default srcset attribute for a responsive original image.
sizes | String | | Default sizes attribute for use with srcset.
sources | Array | | A list of image sources for using the picture tag to serve the appropriate original image (see below for more details).
width | Number | | Width attribute for original image.
height | Number | | Height attribute for original image.
hasSpacer | Boolean | false | If true, gets the original image's aspect ratio based on the width and height props and creates a spacer to prevent cumulative layout shift.
zoomSrc | String | | URL for the larger zoom image. Falls back to original image src if not defined.
zoomScale | Number | 1 | Multiplied against the natural width and height of the zoomed image. This will generally be a decimal (example, 0.9 for 90%).
zoomPreload | Boolean | false | If set to true, preloads the zoom image instead of waiting for mouseenter.
alt | String | | Alternative text for the original image.
moveType | String | pan | `pan` or `drag`. The user behavior for moving zoomed images on non-touch devices.
zoomType | String | click | `click` or `hover`. The zoom behavior for images.
fadeDuration | Number | 150 | Fade transition time in milliseconds. If zooming in on transparent images, set this to `0` for best results.
fullscreenOnMobile | Boolean | false | Enables fullscreen zoomed image on touch devices below a specified breakpoint.
mobileBreakpoint | Number | 640 | The maximum breakpoint for fullscreen zoom image when fullscreenOnMobile is true.
hideCloseButton | Boolean | false | Hides the close button on touch devices. If set to true, zoom out is triggered by tap.
hideHint | Boolean | false | Hides the magnifying glass hint.
className | String | | Custom classname for styling the component.
afterZoomIn | Function | | Function to be called after zoom in.
afterZoomOut | Function | | Function to be called after zoom out.
Expand Down
Binary file added demo/assets/unsplash3-large.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/assets/unsplash3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 19 additions & 3 deletions demo/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,33 @@
<div style="margin-bottom: 30px;">
<h2>Pan Example</h2>
<inner-image-zoom
src="/assets/unsplash.jpg"
zoomSrc="/assets/unsplash-large.jpg"
:src="srcs[0]"
:fullscreenOnMobile="true"
:hideCloseButton="true"
:hideHint="true"
/>
</div>
<div>
<div style="margin-bottom: 30px;">
<h2>Drag Example</h2>
<inner-image-zoom
src="/assets/unsplash2.jpg"
zoomSrc="/assets/unsplash2-large.jpg"
moveType="drag"
:fullscreenOnMobile="true"
:width="750"
:height="500"
:hasSpacer="true"
/>
</div>
<div style="margin-bottom: 30px;">
<h2>Zoom on Hover Example</h2>
<inner-image-zoom
src="/assets/unsplash3.jpg"
zoomSrc="/assets/unsplash3-large.jpg"
zoomType="hover"
:fullscreenOnMobile="true"
:zoomScale="0.9"
/>
</div>
</div>
Expand All @@ -28,6 +43,7 @@ export default {
name: 'App',
components: {
InnerImageZoom
}
},
props: ['srcs']
};
</script>
2 changes: 1 addition & 1 deletion demo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import App from './App.vue';
Vue.config.productionTip = false;

new Vue({
render: (h) => h(App)
render: (h) => h(App, { props: { srcs: ['/assets/unsplash.jpg'] } })
}).$mount('#app');
150 changes: 99 additions & 51 deletions src/InnerImageZoom/InnerImageZoom.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,54 @@
ref="img"
v-bind:class="{
[className]: className,
'iiz--drag': this.currentMoveType === 'drag'
'iiz--drag': currentMoveType === 'drag'
}"
v-bind:style="{
width: `${width}px`
}"
v-on="{
touchstart: handleTouchStart,
touchstart: isZoomed ? () => {} : handleTouchStart,
click: handleClick,
mouseenter: isTouch ? () => {} : handleMouseEnter,
mousemove: currentMoveType === 'drag' || !isZoomed ? () => {} : handleMouseMove,
mouseleave: isTouch ? () => {} : handleMouseLeave
}"
>
<template v-if="validSources">
<picture>
<source
v-for="(source, i) in validSources"
v-bind:key="i"
v-bind:srcSet="source.srcSet"
v-bind:sizes="source.sizes"
v-bind:media="source.media"
v-bind:type="source.type"
/>
<div
v-bind:style="{
paddingTop: createSpacer ? `${(height / width) * 100}%` : null
}"
>
<template v-if="validSources">
<picture>
<source
v-for="(source, i) in validSources"
v-bind:key="i"
v-bind:srcSet="source.srcSet"
v-bind:sizes="source.sizes"
v-bind:media="source.media"
v-bind:type="source.type"
/>
<img
class="iiz__img"
v-bind:class="{ 'iiz__img--hidden': isZoomed, 'iiz__img--abs': createSpacer }"
v-bind:style="{
transition: `linear 0ms opacity ${
isZoomed ? fadeDuration : 0
}ms, linear 0ms visibility ${isZoomed ? fadeDuration : 0}ms`
}"
v-bind:src="src"
v-bind:srcSet="srcSet"
v-bind:sizes="sizes"
v-bind:alt="alt"
/>
</picture>
</template>

<template v-else>
<img
class="iiz__img"
v-bind:class="{ 'iiz__img--invisible': isZoomed }"
v-bind:class="{ 'iiz__img--hidden': isZoomed, 'iiz__img--abs': createSpacer }"
v-bind:style="{
transition: `linear 0ms opacity ${
isZoomed ? fadeDuration : 0
Expand All @@ -37,24 +62,8 @@
v-bind:sizes="sizes"
v-bind:alt="alt"
/>
</picture>
</template>

<template v-else>
<img
class="iiz__img"
v-bind:class="{ 'iiz__img--invisible': isZoomed }"
v-bind:style="{
transition: `linear 0ms opacity ${isZoomed ? fadeDuration : 0}ms, linear 0ms visibility ${
isZoomed ? fadeDuration : 0
}ms`
}"
v-bind:src="src"
v-bind:srcSet="srcSet"
v-bind:sizes="sizes"
v-bind:alt="alt"
/>
</template>
</template>
</div>

<template v-if="isActive">
<template v-if="isFullscreen">
Expand All @@ -63,12 +72,13 @@
<img
class="iiz__zoom-img"
alt=""
draggable="false"
v-bind:class="{ 'iiz__zoom-img--visible': isZoomed }"
v-bind:style="{
top: `${top}px`,
left: `${left}px`,
transition: `linear ${this.isFullscreen ? 0 : fadeDuration}ms opacity, linear ${
this.isFullscreen ? 0 : fadeDuration
transition: `linear ${isFullscreen ? 0 : fadeDuration}ms opacity, linear ${
isFullscreen ? 0 : fadeDuration
}ms visibility`
}"
v-bind:src="zoomSrc || src"
Expand All @@ -77,19 +87,20 @@
touchstart: handleDragStart,
touchend: handleDragEnd,
mousedown: handleDragStart,
mouseup: handleDragEnd
mouseup: handleDragEnd,
click: handleClick
}"
/>

<button
v-if="this.isTouch"
v-if="isTouch && !hideCloseButton"
type="button"
class="iiz__btn iiz__close"
aria-label="Zoom Out"
v-bind:class="{ 'iiz__close--visible': isZoomed }"
v-bind:style="{
transition: `linear ${this.isFullscreen ? 0 : fadeDuration}ms opacity, linear ${
this.isFullscreen ? 0 : fadeDuration
transition: `linear ${isFullscreen ? 0 : fadeDuration}ms opacity, linear ${
isFullscreen ? 0 : fadeDuration
}ms visibility`
}"
v-on:click.stop="handleClose"
Expand All @@ -102,12 +113,13 @@
<img
class="iiz__zoom-img"
alt=""
draggable="false"
v-bind:class="{ 'iiz__zoom-img--visible': isZoomed }"
v-bind:style="{
top: `${top}px`,
left: `${left}px`,
transition: `linear ${this.isFullscreen ? 0 : fadeDuration}ms opacity, linear ${
this.isFullscreen ? 0 : fadeDuration
transition: `linear ${isFullscreen ? 0 : fadeDuration}ms opacity, linear ${
isFullscreen ? 0 : fadeDuration
}ms visibility`
}"
v-bind:src="zoomSrc || src"
Expand All @@ -121,22 +133,22 @@
/>

<button
v-if="this.isTouch"
v-if="isTouch && !hideCloseButton"
class="iiz__btn iiz__close"
type="button"
aria-label="Zoom Out"
v-bind:class="{ 'iiz__close--visible': isZoomed }"
v-bind:style="{
transition: `linear ${this.isFullscreen ? 0 : fadeDuration}ms opacity, linear ${
this.isFullscreen ? 0 : fadeDuration
transition: `linear ${isFullscreen ? 0 : fadeDuration}ms opacity, linear ${
isFullscreen ? 0 : fadeDuration
}ms visibility`
}"
v-on:click.stop="handleClose"
/>
</template>
</template>

<span v-if="!isZoomed" class="iiz__btn iiz__hint"></span>
<span v-if="!isZoomed && !hideHint" class="iiz__btn iiz__hint"></span>
</figure>
</template>

Expand All @@ -153,14 +165,26 @@ export default {
type: String,
default: 'pan'
},
zoomType: {
type: String,
default: 'click'
},
src: {
type: String,
required: true
},
srcSet: String,
sizes: String,
sources: Array,
width: Number,
height: Number,
hasSpacer: Boolean,
zoomSrc: String,
zoomScale: {
type: Number,
default: 1
},
zoomPreload: Boolean,
alt: String,
fadeDuration: {
type: Number,
Expand All @@ -171,13 +195,15 @@ export default {
type: Number,
default: 640
},
hideHint: Boolean,
hideCloseButton: Boolean,
className: String,
afterZoomIn: Function,
afterZoomOut: Function
},
data() {
return {
isActive: false,
isActive: this.zoomPreload || false,
isTouch: false,
isZoomed: false,
isFullscreen: false,
Expand All @@ -194,11 +220,15 @@ export default {
computed: {
validSources: function () {
return this.sources ? this.sources.filter((source) => source.srcSet) : [];
},
createSpacer: function () {
return this.width && this.height && this.hasSpacer;
}
},
methods: {
handleMouseEnter() {
handleMouseEnter(e) {
this.isActive = true;
this.zoomType === 'hover' && !this.isZoomed && this.handleClick(e);
},
handleTouchStart() {
this.isFullscreen =
Expand All @@ -210,8 +240,10 @@ export default {
},
handleClick(e) {
if (this.isZoomed) {
if (!this.isTouch && !this.isDragging) {
this.zoomOut();
if (this.isTouch) {
this.hideCloseButton && this.handleClose();
} else {
!this.isDragging && this.zoomOut();
}
return;
Expand All @@ -221,15 +253,22 @@ export default {
this.isActive = true;
}
if (this.imgProps.isLoaded) {
if (this.imgProps.zoomImg) {
this.zoomIn(e.pageX, e.pageY);
} else {
this.imgProps.onLoadCallback = this.zoomIn.bind(this, e.pageX, e.pageY);
}
},
handleLoad(e) {
this.imgProps.isLoaded = true;
this.imgProps.zoomImg = e.target;
this.imgProps.zoomImg.setAttribute(
'width',
this.imgProps.zoomImg.naturalWidth * this.zoomScale
);
this.imgProps.zoomImg.setAttribute(
'height',
this.imgProps.zoomImg.naturalHeight * this.zoomScale
);
this.imgProps.bounds = getBounds(this.$refs.img, false);
this.imgProps.ratios = getRatios(this.imgProps.bounds, this.imgProps.zoomImg);
Expand Down Expand Up @@ -368,7 +407,6 @@ export default {
}
},
setDefaults() {
this.imgProps.isLoaded = false;
this.imgProps.onLoadCallback = null;
this.imgProps.zoomImg = null;
this.imgProps.bounds = {};
Expand Down Expand Up @@ -409,6 +447,7 @@ function getRatios(bounds, zoomImg) {

<style scoped>
.iiz {
max-width: 100%;
margin: 0;
position: relative;
overflow: hidden;
Expand All @@ -429,11 +468,20 @@ function getRatios(bounds, zoomImg) {
opacity: 1;
}
.iiz__img--invisible {
.iiz__img--hidden {
visibility: hidden;
opacity: 0;
}
.iiz__img--abs {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
display: block;
}
.iiz__zoom-img {
width: auto !important;
max-width: none !important;
Expand Down
Loading

0 comments on commit 9b4ec75

Please sign in to comment.