ive got a Vue component which lets me zoom in on an image, using the mouse wheel works fine, however pinch zoom behaves strangely.
Basically if you put 2 fingers on the screen far apart it zooms in a lot, put them in closer together and it zooms in a bit. What should happen is it doesn't zoom in or out until you actually move your fingers in or out.
Any ideas?
Vue.component('test', {
data() {
return {
loading: false,
loop: true,
speed: 8,
speedController: 0,
zoomEnabled: true,
zoomLevels: [1, 1.5, 2, 2.5, 3],
zoomLevel: 1,
frame: 1,
images: [],
imagesPreloaded: 0,
spinEnabled: true,
spinAuto: false,
reverse: false,
viewportScale: 0.3,
viewportEnabled: true,
viewportOpacity: 0.8,
lastX: 0,
lastY: 0,
startX: 0,
startY: 0,
translateX: 0,
translateY: 0,
isMoving: false,
isDragging: false,
lastPinch: 0,
animationRequestID: 0,
spinStart: null,
spinThen: Date.now(),
fps: 1000 / 8,
axiosRequest: null,
$clickEvent: null,
$moveEvent: null,
output: ''
};
},
mounted() {
window.addEventListener('mouseup', this.handleEnd);
window.addEventListener('touchend', this.handleEnd);
window.addEventListener('resize', this.fetch);
},
beforeDestroy() {
window.removeEventListener('mouseup', this.handleEnd);
window.removeEventListener('touchend', this.handleEnd);
},
methods: {
handleSlider(event) {
this.frame = Number(event.target.value);
},
zoom(direction) {
if (this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) + direction] === undefined) {
return;
}
let current = this.zoomLevels.indexOf(this.closestZoom);
let index = current += direction;
if (direction === 0) {
index = 0;
}
this.zoomLevel = this.zoomLevels[index];
this.translate(null, true);
},
zoomWheel($event) {
this.zoomLevel += $event.deltaY * -0.01;
if (this.zoomLevel < 1) {
this.zoomLevel = 1;
}
$event.preventDefault();
let maxZoom = this.zoomLevels[this.zoomLevels.length - 1];
this.zoomLevel = Math.min(Math.max(.125, this.zoomLevel), maxZoom);
this.translate(null, true);
},
zoomPinch($event) {
let curDiff = Math.abs($event.touches[0].clientX - $event.touches[1].clientX);
$event.deltaY = this.lastPinch - curDiff;
this.zoomWheel($event);
this.lastPinch = curDiff;
},
handleStart($event) {
if ($event.button && $event.button !== 0) {
return;
}
this.$clickEvent = $event;
if (this.animationRequestID !== 0) {
this.spinStop();
}
this.isMoving = true;
this.isDragging = true;
// this.startTouchX = [ $event.touches[0].clientX, $event.touches[1].clientX ];
// this.startTouchY = [ [ $event.touches[0].clientY, $event.touches[1].clientY ] ];
this.startX = this.$clickEvent.pageX || this.$clickEvent.touches[0].pageX;
this.startY = this.$clickEvent.pageY || this.$clickEvent.touches[0].pageY;
},
handleMove($event, viewport) {
if ($event.button && $event.button !== 0) {
return;
}
if ($event.touches && $event.touches.length > 1) {
this.zoomPinch($event);
return;
}
this.$moveEvent = $event;
if (this.isMoving && this.isDragging) {
const positions = {
x: $event.pageX || $event.touches[0].pageX,
y: $event.pageY || $event.touches[0].pageY
}
if (this.zoomLevel !== 1) {
this.translate(positions, null, viewport);
}
if (this.zoomLevel === 1) {
this.changeFrame(positions);
}
this.lastX = positions.x;
this.lastY = positions.y;
}
},
handleEnd($event) {
if ($event.button && $event.button !== 0) {
return;
}
this.isMoving = false;
},
spin(index) {
let i = index;
if (i >= this.images.length) {
i = 1;
}
this.animationRequestID = window.requestAnimationFrame(() => this.spin(i));
let now = Date.now();
let elapsed = now - this.spinThen;
if (elapsed > this.fps) {
this.spinThen = now - (elapsed % this.fps);
this.frame = i;
i += 1;
}
},
spinToggle() {
if (this.animationRequestID === 0 && this.zoomLevel === 1) {
this.spin(this.frame);
return;
}
this.spinStop();
},
spinStop() {
if (this.animationRequestID) {
window.cancelAnimationFrame(this.animationRequestID);
this.animationRequestID = 0;
}
},
translate(positions, zooming, viewport) {
if (this.$moveEvent) {
this.$moveEvent.preventDefault();
}
window.requestAnimationFrame(() => {
positions = positions || {
x: this.startX,
y: this.startY
};
if (viewport) {
this._translateFromViewport(positions);
} else {
this._translateFromImage(positions, zooming);
}
this.startX = positions.x;
this.startY = positions.y;
});
},
/**
* #param positions
* #private
*/
_translateFromViewport: function(positions) {
let move = {
x: Math.floor(positions.x - this.startX),
y: Math.floor(positions.y - this.startY)
};
let box = this.$refs.viewportBox.getBoundingClientRect();
let container = this.$refs.viewportContainer.getBoundingClientRect();
// Amount of pixels moved within animation frame, adjust based on viewport scale.
// Zoom level doesn't matter as image scale doesn't move, so box is moving same amount of pixels.
let moveAmountX = (move.x / this.viewportScale);
let moveAmountY = (move.y / this.viewportScale);
// Find the current offset of the container bounds, calculate new offset based on movement amount
let calculatedOffset = {
left: (container.left - box.left) - moveAmountX,
right: (container.right - box.right) - moveAmountX,
top: (container.top - box.top) - moveAmountY,
bottom: (container.bottom - box.bottom) - moveAmountY
};
this.output = JSON.stringify(calculatedOffset);
// Only move if the calculated new offset is not out of container bounds
// Reverse the movement as moving box in same direction as cursor rather than the image.
if (calculatedOffset.left <= 0 && calculatedOffset.right >= 0) {
this.translateX += -moveAmountX;
}
if (calculatedOffset.top <= 0 && calculatedOffset.bottom >= 0) {
this.translateY += -moveAmountY;
}
},
_translateFromImage: function(positions, zooming) {
let move = {
x: Math.floor(positions.x - this.startX),
y: Math.floor(positions.y - this.startY)
};
let image = this.$refs.image.getBoundingClientRect();
let container = this.$refs.container.getBoundingClientRect();
let moveAmountX = move.x * this.zoomLevel;
let moveAmountY = move.y * this.zoomLevel;
let calculatedOffset = {
left: (container.left - image.left) - moveAmountX,
right: (container.right - image.right) - moveAmountX,
top: (container.top - image.top) - moveAmountY,
bottom: (container.bottom - image.bottom) - moveAmountY
};
if (zooming) {
if (calculatedOffset.left <= 0) {
this.translateX += calculatedOffset.left;
}
if (calculatedOffset.right >= 0) {
this.translateX += calculatedOffset.right;
}
if (calculatedOffset.top <= 0) {
this.translateY += calculatedOffset.top;
}
if (calculatedOffset.bottom >= 0) {
this.translateY += calculatedOffset.bottom;
}
}
if (calculatedOffset.left >= 0 && calculatedOffset.right <= 0) {
this.translateX += move.x / this.zoomLevel;
}
if (calculatedOffset.top >= 0 && calculatedOffset.bottom <= 0) {
this.translateY += move.y / this.zoomLevel;
}
},
changeFrame(positions) {
this.speedController += 1;
if (this.speedController < this.speed) {
return;
}
if (this.speedController > this.speed) {
this.speedController = 0;
}
if (positions.x > this.lastX) {
if (this.frame >= 0 && this.frame < this.images.length) {
this.frame += 1;
} else if (this.loop) {
this.frame = 1;
}
} else if (positions.x < this.lastX) {
if (this.frame >= 0 && this.frame - 1 > 0) {
this.frame -= 1;
} else if (this.loop) {
this.frame = this.images.length;
}
}
}
},
watch: {
zoomLevel: function() {
if (this.zoomLevel !== 1 && this.animationRequestID !== 0) {
this.spinStop();
}
}
},
computed: {
closestZoom: function() {
return this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
},
imageSet: function() {
return this.images.map(image => {
return image[this.closestZoom].url;
});
},
preloadProgress: function() {
return Math.floor(this.imagesPreloaded / this.images.length * 100);
},
currentPath: function() {
return this.images[this.frame - 1][this.closestZoom].url;
},
nextZoomLevel: function() {
if (this.zoomLevels.indexOf(this.closestZoom) === this.zoomLevels.length - 1) {
return this.zoomLevels[0];
}
return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) + 1];
},
viewportTransform: function() {
if (this.viewportEnabled) {
let translateX = -((this.translateX * this.viewportScale) * this.zoomLevel);
let translateY = -((this.translateY * this.viewportScale) * this.zoomLevel);
return `scale(${1 / this.zoomLevel}) translateX(${translateX}px) translateY(${translateY}px)`;
}
},
transform: function() {
return `scale(${this.zoomLevel}) translateX(${this.translateX}px) translateY(${this.translateY}px)`;
},
canZoomIn: function() {
return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) + 1] === undefined
},
canZoomOut: function() {
return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) + -1] === undefined
}
},
template: '#template'
});
window.vue = new Vue({}).$mount('#app');
.media-360-viewer {
position: relative;
overflow: hidden;
display: inline-block;
background: #000;
width: 100%;
transition: filter .2s ease-in-out;
&__image {
width: 100%;
cursor: grab;
&.isTranslating {
cursor: grabbing;
}
&.loading {
filter: blur(4px);
}
}
&__loader {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .5);
* {
user-select: none;
}
&>svg {
width: 100%;
height: 100%;
transform: rotate(270deg);
}
&--text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
p {
font-size: 100%;
font-weight: bold;
color: #fff;
&.large {
font-size: 150%;
}
}
}
&--background {
stroke-dasharray: 0;
stroke-dashoffset: 0;
stroke: rgba(0, 0, 0, .7);
stroke-width: 25px;
}
&--cover {
stroke-dasharray: 200%;
stroke: #848484;
stroke-width: 15px;
stroke-linecap: round;
}
&--background,
&--cover {
fill: transparent;
}
}
&__viewport {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
overflow: hidden;
&--image {
width: 100%;
pointer-events: none;
}
&--zoom {
position: absolute;
bottom: 5px;
right: 5px;
color: #fff;
z-index: 3;
font-size: 12px;
pointer-events: none;
}
&--square {
display: block;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
box-shadow: rgba(0, 0, 0, .6) 0 0 0 10000px;
cursor: grab;
transition: background ease-in-out .1s;
&:hover {
background: rgba(255, 255, 255, .2);
}
}
}
&__tools {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 10px;
&>a {
margin: 0 5px;
color: #000;
background: #fff;
border-radius: 50%;
width: 40px;
text-align: center;
line-height: 40px;
&[disabled] {
opacity: .5;
cursor: not-allowed;
&:hover {
color: #000;
background: #fff;
}
}
&:hover {
background: #000;
color: #fff;
}
}
&--autoplay {
&:before {
font-family: 'ClickIcons';
content: '\ea81';
}
&.active:before {
content: '\eb48';
}
}
}
}
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<test class="test"></test>
</div>
<script type="text/x-template" id="template">
<div>
<div class="media-360-viewer" ref="container">
<img tabindex="1" ref="image" draggable="false" src="https://s3-eu-west-1.amazonaws.com/crash.net/visordown.com/styles/amp_1200/s3/2020_YAM_YZF1000R1SPL_EU_BWM2_STA_001-70560.jpg?itok=5bisLKmj" :style="{ transform: transform }" class="media-360-viewer__image"
#touchend="handleEnd" #touchmove="handleMove" #touchstart="handleStart" #wheel="zoomWheel" alt="360 Image" />
</div>
</div>
</script>
I'm not sure I understand your problem correctly but as I try I think the problem of your code is on this line:
$event.deltaY = this.lastPinch - curDiff;
It seems this.lastPinch is hold the delta of previous touchmove event so for the first time of event you should ignore it and clear when touchend.
...
zoomPinch ($event) {
...
if (this.lastPinch) {
$event.deltaY = this.lastPinch - curDiff;
this.zoomWheel($event);
}
...
}
...
handleEnd ($event) {
...
this.lastPinch = 0
}
...
Vue.component('test', {
data() {
return {
loading: false,
loop: true,
speed: 8,
speedController: 0,
zoomEnabled: true,
zoomLevels: [1, 1.5, 2, 2.5, 3],
zoomLevel: 1,
frame: 1,
images: [],
imagesPreloaded: 0,
spinEnabled: true,
spinAuto: false,
reverse: false,
viewportScale: 0.3,
viewportEnabled: true,
viewportOpacity: 0.8,
lastX: 0,
lastY: 0,
startX: 0,
startY: 0,
translateX: 0,
translateY: 0,
isMoving: false,
isDragging: false,
lastPinch: 0,
animationRequestID: 0,
spinStart: null,
spinThen: Date.now(),
fps: 1000 / 8,
axiosRequest: null,
$clickEvent: null,
$moveEvent: null,
output: ''
};
},
mounted() {
window.addEventListener('mouseup', this.handleEnd);
window.addEventListener('touchend', this.handleEnd);
window.addEventListener('resize', this.fetch);
},
beforeDestroy() {
window.removeEventListener('mouseup', this.handleEnd);
window.removeEventListener('touchend', this.handleEnd);
},
methods: {
handleSlider(event) {
this.frame = Number(event.target.value);
},
zoom(direction) {
if (this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) + direction] === undefined) {
return;
}
let current = this.zoomLevels.indexOf(this.closestZoom);
let index = current += direction;
if (direction === 0) {
index = 0;
}
this.zoomLevel = this.zoomLevels[index];
this.translate(null, true);
},
zoomWheel($event) {
this.zoomLevel += $event.deltaY * -0.01;
if (this.zoomLevel < 1) {
this.zoomLevel = 1;
}
$event.preventDefault();
let maxZoom = this.zoomLevels[this.zoomLevels.length - 1];
this.zoomLevel = Math.min(Math.max(.125, this.zoomLevel), maxZoom);
this.translate(null, true);
},
zoomPinch($event) {
let curDiff = Math.abs($event.touches[0].clientX - $event.touches[1].clientX);
if (this.lastPinch) {
$event.deltaY = this.lastPinch - curDiff;
this.zoomWheel($event);
}
this.lastPinch = curDiff;
},
handleStart($event) {
if ($event.button && $event.button !== 0) {
return;
}
this.$clickEvent = $event;
if (this.animationRequestID !== 0) {
this.spinStop();
}
this.isMoving = true;
this.isDragging = true;
// this.startTouchX = [ $event.touches[0].clientX, $event.touches[1].clientX ];
// this.startTouchY = [ [ $event.touches[0].clientY, $event.touches[1].clientY ] ];
this.startX = this.$clickEvent.pageX || this.$clickEvent.touches[0].pageX;
this.startY = this.$clickEvent.pageY || this.$clickEvent.touches[0].pageY;
},
handleMove($event, viewport) {
if ($event.button && $event.button !== 0) {
return;
}
if ($event.touches && $event.touches.length > 1) {
this.zoomPinch($event);
return;
}
this.$moveEvent = $event;
if (this.isMoving && this.isDragging) {
const positions = {
x: $event.pageX || $event.touches[0].pageX,
y: $event.pageY || $event.touches[0].pageY
}
if (this.zoomLevel !== 1) {
this.translate(positions, null, viewport);
}
if (this.zoomLevel === 1) {
this.changeFrame(positions);
}
this.lastX = positions.x;
this.lastY = positions.y;
}
},
handleEnd($event) {
if ($event.button && $event.button !== 0) {
return;
}
this.isMoving = false;
this.lastPinch = 0;
},
spin(index) {
let i = index;
if (i >= this.images.length) {
i = 1;
}
this.animationRequestID = window.requestAnimationFrame(() => this.spin(i));
let now = Date.now();
let elapsed = now - this.spinThen;
if (elapsed > this.fps) {
this.spinThen = now - (elapsed % this.fps);
this.frame = i;
i += 1;
}
},
spinToggle() {
if (this.animationRequestID === 0 && this.zoomLevel === 1) {
this.spin(this.frame);
return;
}
this.spinStop();
},
spinStop() {
if (this.animationRequestID) {
window.cancelAnimationFrame(this.animationRequestID);
this.animationRequestID = 0;
}
},
translate(positions, zooming, viewport) {
if (this.$moveEvent) {
this.$moveEvent.preventDefault();
}
window.requestAnimationFrame(() => {
positions = positions || {
x: this.startX,
y: this.startY
};
if (viewport) {
this._translateFromViewport(positions);
} else {
this._translateFromImage(positions, zooming);
}
this.startX = positions.x;
this.startY = positions.y;
});
},
/**
* #param positions
* #private
*/
_translateFromViewport: function(positions) {
let move = {
x: Math.floor(positions.x - this.startX),
y: Math.floor(positions.y - this.startY)
};
let box = this.$refs.viewportBox.getBoundingClientRect();
let container = this.$refs.viewportContainer.getBoundingClientRect();
// Amount of pixels moved within animation frame, adjust based on viewport scale.
// Zoom level doesn't matter as image scale doesn't move, so box is moving same amount of pixels.
let moveAmountX = (move.x / this.viewportScale);
let moveAmountY = (move.y / this.viewportScale);
// Find the current offset of the container bounds, calculate new offset based on movement amount
let calculatedOffset = {
left: (container.left - box.left) - moveAmountX,
right: (container.right - box.right) - moveAmountX,
top: (container.top - box.top) - moveAmountY,
bottom: (container.bottom - box.bottom) - moveAmountY
};
this.output = JSON.stringify(calculatedOffset);
// Only move if the calculated new offset is not out of container bounds
// Reverse the movement as moving box in same direction as cursor rather than the image.
if (calculatedOffset.left <= 0 && calculatedOffset.right >= 0) {
this.translateX += -moveAmountX;
}
if (calculatedOffset.top <= 0 && calculatedOffset.bottom >= 0) {
this.translateY += -moveAmountY;
}
},
_translateFromImage: function(positions, zooming) {
let move = {
x: Math.floor(positions.x - this.startX),
y: Math.floor(positions.y - this.startY)
};
let image = this.$refs.image.getBoundingClientRect();
let container = this.$refs.container.getBoundingClientRect();
let moveAmountX = move.x * this.zoomLevel;
let moveAmountY = move.y * this.zoomLevel;
let calculatedOffset = {
left: (container.left - image.left) - moveAmountX,
right: (container.right - image.right) - moveAmountX,
top: (container.top - image.top) - moveAmountY,
bottom: (container.bottom - image.bottom) - moveAmountY
};
if (zooming) {
if (calculatedOffset.left <= 0) {
this.translateX += calculatedOffset.left;
}
if (calculatedOffset.right >= 0) {
this.translateX += calculatedOffset.right;
}
if (calculatedOffset.top <= 0) {
this.translateY += calculatedOffset.top;
}
if (calculatedOffset.bottom >= 0) {
this.translateY += calculatedOffset.bottom;
}
}
if (calculatedOffset.left >= 0 && calculatedOffset.right <= 0) {
this.translateX += move.x / this.zoomLevel;
}
if (calculatedOffset.top >= 0 && calculatedOffset.bottom <= 0) {
this.translateY += move.y / this.zoomLevel;
}
},
changeFrame(positions) {
this.speedController += 1;
if (this.speedController < this.speed) {
return;
}
if (this.speedController > this.speed) {
this.speedController = 0;
}
if (positions.x > this.lastX) {
if (this.frame >= 0 && this.frame < this.images.length) {
this.frame += 1;
} else if (this.loop) {
this.frame = 1;
}
} else if (positions.x < this.lastX) {
if (this.frame >= 0 && this.frame - 1 > 0) {
this.frame -= 1;
} else if (this.loop) {
this.frame = this.images.length;
}
}
}
},
watch: {
zoomLevel: function() {
if (this.zoomLevel !== 1 && this.animationRequestID !== 0) {
this.spinStop();
}
}
},
computed: {
closestZoom: function() {
return this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
},
imageSet: function() {
return this.images.map(image => {
return image[this.closestZoom].url;
});
},
preloadProgress: function() {
return Math.floor(this.imagesPreloaded / this.images.length * 100);
},
currentPath: function() {
return this.images[this.frame - 1][this.closestZoom].url;
},
nextZoomLevel: function() {
if (this.zoomLevels.indexOf(this.closestZoom) === this.zoomLevels.length - 1) {
return this.zoomLevels[0];
}
return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) + 1];
},
viewportTransform: function() {
if (this.viewportEnabled) {
let translateX = -((this.translateX * this.viewportScale) * this.zoomLevel);
let translateY = -((this.translateY * this.viewportScale) * this.zoomLevel);
return `scale(${1 / this.zoomLevel}) translateX(${translateX}px) translateY(${translateY}px)`;
}
},
transform: function() {
return `scale(${this.zoomLevel}) translateX(${this.translateX}px) translateY(${this.translateY}px)`;
},
canZoomIn: function() {
return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) + 1] === undefined
},
canZoomOut: function() {
return this.zoomLevels[this.zoomLevels.indexOf(this.closestZoom) + -1] === undefined
}
},
template: '#template'
});
window.vue = new Vue({}).$mount('#app');
.media-360-viewer {
position: relative;
overflow: hidden;
display: inline-block;
background: #000;
width: 100%;
transition: filter .2s ease-in-out;
&__image {
width: 100%;
cursor: grab;
&.isTranslating {
cursor: grabbing;
}
&.loading {
filter: blur(4px);
}
}
&__loader {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .5);
* {
user-select: none;
}
&>svg {
width: 100%;
height: 100%;
transform: rotate(270deg);
}
&--text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
p {
font-size: 100%;
font-weight: bold;
color: #fff;
&.large {
font-size: 150%;
}
}
}
&--background {
stroke-dasharray: 0;
stroke-dashoffset: 0;
stroke: rgba(0, 0, 0, .7);
stroke-width: 25px;
}
&--cover {
stroke-dasharray: 200%;
stroke: #848484;
stroke-width: 15px;
stroke-linecap: round;
}
&--background,
&--cover {
fill: transparent;
}
}
&__viewport {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
overflow: hidden;
&--image {
width: 100%;
pointer-events: none;
}
&--zoom {
position: absolute;
bottom: 5px;
right: 5px;
color: #fff;
z-index: 3;
font-size: 12px;
pointer-events: none;
}
&--square {
display: block;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
box-shadow: rgba(0, 0, 0, .6) 0 0 0 10000px;
cursor: grab;
transition: background ease-in-out .1s;
&:hover {
background: rgba(255, 255, 255, .2);
}
}
}
&__tools {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 10px;
&>a {
margin: 0 5px;
color: #000;
background: #fff;
border-radius: 50%;
width: 40px;
text-align: center;
line-height: 40px;
&[disabled] {
opacity: .5;
cursor: not-allowed;
&:hover {
color: #000;
background: #fff;
}
}
&:hover {
background: #000;
color: #fff;
}
}
&--autoplay {
&:before {
font-family: 'ClickIcons';
content: '\ea81';
}
&.active:before {
content: '\eb48';
}
}
}
}
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<test class="test"></test>
</div>
<script type="text/x-template" id="template">
<div>
<div class="media-360-viewer" ref="container">
<img tabindex="1" ref="image" draggable="false" src="https://s3-eu-west-1.amazonaws.com/crash.net/visordown.com/styles/amp_1200/s3/2020_YAM_YZF1000R1SPL_EU_BWM2_STA_001-70560.jpg?itok=5bisLKmj" :style="{ transform: transform }" class="media-360-viewer__image"
#touchend="handleEnd" #touchmove="handleMove" #touchstart="handleStart" #wheel="zoomWheel" alt="360 Image" />
</div>
</div>
</script>
Related
I am making a John Conway's game of life and when I try run it by pressing space, it is actually bigger than what the console shows it to be. I set the div to be exactly 0.1% of the bigger grid which is in dark blue. But it seems to just be bigger or smaller. I also use panning and zooming for the project
//Important variable
var mainGrid = document.querySelector(".main-grid")
var windowWidth = document.documentElement.clientWidth
var windowHeight = document.documentElement.clientHeight
var time = 500
var Data = {
livingCells: [
[3,0],
[5,0],
[4,1],
[5,1]
]
}
//functions
var updateCells = (rle) => {
if (!rle) {
$(".main-grid").empty()
for (let i = 0; i < Data.livingCells.length; i++) {
const element = Data.livingCells[i];
$(".main-grid").append('<div id="'+i+'" class="on"></div>')
$('#'+i+'').css({ 'left': element[0]/10+'%', 'top': element[1]/10+'%' ,})
console.log(element[1])
}
}
}
var ID;
var cellInterval = () => {
ID = setInterval(() => {
updateCells()
}, time)
}
var intervalOn = false;
//listeners
document.body.onkeyup = function(e) {
if (e.key == " " ||
e.code == "Space" ||
e.keyCode == 32
) {
if(!intervalOn){
cellInterval()
intervalOn = true;
console.log("interval on")
} else {
clearInterval(ID)
intervalOn = false;
console.log("interval off")
}
}
}
// panzoom
panzoom(mainGrid, {
minZoom: 0.3,
maxZoom: 10,
initialZoom: 1.5,
initialX: mainGrid.offsetWidth/2,
initialY: mainGrid.offsetWidth,
bounds: true,
boundsPadding: 0.3
});
* {
padding: 0;
margin: 0;
outline: 0;
overflow: hidden;
}
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: gray;
}
.main-grid{
width: 100vw;
height: 100vw;
background-color: rgb(0, 0, 0);
}
.hover:hover{
transition: 0.3s;
background-color: rgb(79, 124, 182);
transform: scale(1.2);
}
.on{
position: fixed;
height: 0.1%;
width: 0.1%;
background-color: white;
}
<body>
<div class="main-grid">
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="crossorigin="anonymous"></script>
<script src='https://unpkg.com/panzoom#9.4.0/dist/panzoom.min.js'></script>
<script src="app.js"></script>
</body>
Have you tried Include
{
box-sizing: border-box;
}
I have a animation whose duration decreases each time black jumps(using space) over the red, it works fine that subsequent jumps reduces the duration.
But after a certain decrease in time, say after reducing to 4s,3.9s,3.8s..., the animation don't start from the right-most end which is supposed to be. As it is decided path(110vw) in #keyframes animateVillan
Is there something I am doing wrong, first thought it is a glitch kind and decided to change duration only when red reaches less than 10 and tried below part
if (ourVillanFigXValue < 10) {
ourVillanFig.style.animationDuration = ourVillanAnimeDuration - 0.1 + "s";
}
But this didn't solve the problem and path is not completely traced by the red
Sorry have to jump a little, 4 or 5 jumps only to see the error plz
let ourHeroFig = document.getElementById("ourHero");
let ourVillanFig = document.getElementById("obstacleBar");
let gameScoreDigits = document.getElementById("gameScoreDigits");
let valueXCoordinate = "";
let obstacleBarCrossed = true;
document.body.addEventListener('keydown', function(e) {
let ourHeroFigXValue = parseInt(getComputedStyle(ourHeroFig).getPropertyValue('left'));
let ourHeroFigYValue = parseInt(getComputedStyle(ourHeroFig).getPropertyValue('bottom'));
if (e.code === "ArrowRight") {
valueXCoordinate = ourHeroFigXValue + 100;
} else if (e.code === "KeyA" || e.code === "ArrowLeft") {
if (ourHeroFigXValue > ourHeroFig.offsetWidth + 90) {
valueXCoordinate = ourHeroFigXValue - 100;
} else {
valueXCoordinate = 0;
}
} else if (e.code === "Space") {
ourHeroFig.classList.add("animateHero");
setTimeout(function() {
ourHeroFig.classList.remove("animateHero");
}, 700)
}
changePosition();
})
function changePosition() {
ourHeroFig.style.left = valueXCoordinate + 'px'
}
let delayInAnimeSub = ""
setInterval(
function() {
let ourHeroFigXValue = parseInt(getComputedStyle(ourHeroFig).getPropertyValue('left'));
let ourHeroFigYValue = parseInt(getComputedStyle(ourHeroFig).getPropertyValue('bottom'));
let ourVillanFigXValue = parseInt(getComputedStyle(ourVillanFig).getPropertyValue('left'));
let ourVillanFigYValue = parseInt(getComputedStyle(ourVillanFig).getPropertyValue('bottom'));
let gameOverValueX = Math.abs(ourVillanFigXValue - ourHeroFigXValue);
let gameOverValueY = Math.abs(ourVillanFigYValue - ourHeroFigYValue);
if (ourVillanFigXValue < 10) {
ourVillanFig.style.animationDuration = ourVillanAnimeDuration - 0.3 + "s";
}
if (gameOverValueX < ourVillanFig.offsetWidth && gameOverValueY < ourVillanFig.offsetHeight) {
console.log("yes touched");
ourVillanFig.classList.remove("animateVillan");
obstacleBarCrossed = false;
} else if (obstacleBarCrossed && gameOverValueX < ourVillanFig.offsetWidth) {
ourVillanAnimeDuration = parseFloat(getComputedStyle(ourVillanFig).getPropertyValue('animation-duration'));
console.log(ourVillanFigXValue < 0, ourVillanAnimeDuration)
if (ourVillanAnimeDuration <= 2) {
ourVillanAnimeDuration = 2
}
}
// console.log(gameOverValueX,gameOverValueY)
}, 10);
#ourHero {
width: 20px;
height: 180px;
background-color: black;
position: fixed;
bottom: 0;
left: 0;
transition: 0.1s;
}
.animateHero {
animation: animateHero 0.7s linear;
}
#keyframes animateHero {
0% {
bottom: 0;
}
50% {
bottom: 350px;
}
100% {
bottom: 0;
}
}
#obstacleBar {
width: 20px;
height: 180px;
background-color: red;
position: fixed;
bottom: 0;
left: 50vw;
}
.animateVillan {
animation: animateVillan 5s linear infinite;
}
#keyframes animateVillan {
0% {
left: 110vw;
}
100% {
left: 0;
}
}
<div id="ourHero"></div>
<div id="obstacleBar" class="animateVillan"></div>
Thanks for help in advance
First make the red thing starts from far right so change left: 50vw; to something like left: 110vw;. Also, instead of the infinite animation, just remove the animateVillan class after the red gets out of screen then re-add it again. Can be done by using an animationend event handler:
ourVillanFig.addEventListener('animationend', () => {
ourVillanFig.classList.remove('animateVillan')
ourVillanFig.clientHeight // just to cause a reflow
ourVillanFig.classList.add('animateVillan')
})
or maybe by checking its x position and see when it gets out.
Here is the result. It seems that it is working just fine without any glitches:
EDIT: To make the red stops where it is when it touches black, you can make the following css class:
.pauseVillan {
animation-play-state: paused;
}
and then when the red gets in touch just add the pauseVillan class to it:
ourVillanFig.classList.add('pauseVillan')
Here is the updated snippet:
let ourHeroFig = document.getElementById('ourHero')
let ourVillanFig = document.getElementById('obstacleBar')
let gameScoreDigits = document.getElementById('gameScoreDigits')
let valueXCoordinate = ''
let obstacleBarCrossed = true
document.body.addEventListener('keydown', function(e) {
let ourHeroFigXValue = parseInt(
getComputedStyle(ourHeroFig).getPropertyValue('left')
)
let ourHeroFigYValue = parseInt(
getComputedStyle(ourHeroFig).getPropertyValue('bottom')
)
if (e.code === 'ArrowRight') {
valueXCoordinate = ourHeroFigXValue + 100
} else if (e.code === 'KeyA' || e.code === 'ArrowLeft') {
if (ourHeroFigXValue > ourHeroFig.offsetWidth + 90) {
valueXCoordinate = ourHeroFigXValue - 100
} else {
valueXCoordinate = 0
}
} else if (e.code === 'Space') {
ourHeroFig.classList.add('animateHero')
setTimeout(function() {
ourHeroFig.classList.remove('animateHero')
}, 700)
}
changePosition()
})
ourVillanFig.addEventListener('animationend', () => {
ourVillanFig.classList.remove('animateVillan')
ourVillanFig.clientHeight // just to cause a reflow
ourVillanFig.classList.add('animateVillan')
})
function changePosition() {
ourHeroFig.style.left = valueXCoordinate + 'px'
}
let delayInAnimeSub = ''
setInterval(function() {
let ourHeroFigXValue = parseInt(
getComputedStyle(ourHeroFig).getPropertyValue('left')
)
let ourHeroFigYValue = parseInt(
getComputedStyle(ourHeroFig).getPropertyValue('bottom')
)
let ourVillanFigXValue = parseInt(
getComputedStyle(ourVillanFig).getPropertyValue('left')
)
let ourVillanFigYValue = parseInt(
getComputedStyle(ourVillanFig).getPropertyValue('bottom')
)
let gameOverValueX = Math.abs(ourVillanFigXValue - ourHeroFigXValue)
let gameOverValueY = Math.abs(ourVillanFigYValue - ourHeroFigYValue)
if (ourVillanFigXValue < 10) {
ourVillanFig.style.animationDuration =
ourVillanAnimeDuration - 0.3 + 's'
}
if (
gameOverValueX < ourVillanFig.offsetWidth &&
gameOverValueY < ourVillanFig.offsetHeight
) {
console.log('yes touched')
ourVillanFig.classList.add('pauseVillan')
obstacleBarCrossed = false
} else if (
obstacleBarCrossed &&
gameOverValueX < ourVillanFig.offsetWidth
) {
ourVillanAnimeDuration = parseFloat(
getComputedStyle(ourVillanFig).getPropertyValue('animation-duration')
)
console.log(ourVillanFigXValue < 0, ourVillanAnimeDuration)
if (ourVillanAnimeDuration <= 2) {
ourVillanAnimeDuration = 2
}
}
// console.log(gameOverValueX,gameOverValueY)
}, 10)
#ourHero {
width: 20px;
height: 180px;
background-color: black;
position: fixed;
bottom: 0;
left: 0;
transition: 0.1s;
}
.animateHero {
animation: animateHero 0.7s linear;
}
#keyframes animateHero {
0% {
bottom: 0;
}
50% {
bottom: 350px;
}
100% {
bottom: 0;
}
}
#obstacleBar {
width: 20px;
height: 180px;
background-color: red;
position: fixed;
bottom: 0;
left: 110vw;
}
.animateVillan {
animation: animateVillan 4s linear;
}
.pauseVillan {
animation-play-state: paused;
}
#keyframes animateVillan {
0% {
left: 110vw;
}
100% {
left: 0;
}
}
<div id="ourHero"></div>
<div id="obstacleBar" class="animateVillan"></div>
Goodday Coders, Im struggling with this puzzle script that I want to use for a website's "meet the team" page.
I would like the puzzle to scramble as the page loads instead of having to press the shuffle button.
If people press the "Wie ben ik" button, the puzzle should solve, like it is doing right now.
Somehow I cant get it to work, it would be great if someone could help me out.
Here's the codepen link:
https://codepen.io/verberne/pen/WNxyprV
// Begin game once DOM loaded
document.addEventListener("DOMContentLoaded", game);
// document.addEventListener("DOMContentLoaded", shuffleTimeouts);
function game() {
// Data structure to hold positions of tiles
var parentX = document.querySelector(".sliding-puzzle").clientHeight;
var baseDistance = 38;
var tileMap = {
1: {
tileNumber: 1,
position: 1,
top: 0,
left: 0
},
2: {
tileNumber: 2,
position: 2,
top: 0,
left: baseDistance * 1
},
3: {
tileNumber: 3,
position: 3,
top: 0,
left: baseDistance * 2
},
4: {
tileNumber: 4,
position: 4,
top: baseDistance,
left: 0
},
5: {
tileNumber: 5,
position: 5,
top: baseDistance,
left: baseDistance
},
6: {
tileNumber: 6,
position: 6,
top: baseDistance,
left: baseDistance * 2
},
7: {
tileNumber: 7,
position: 7,
top: baseDistance * 2,
left: 0
},
8: {
tileNumber: 8,
position: 8,
top: baseDistance * 2,
left: baseDistance
},
empty: {
position: 9,
top: baseDistance * 2,
left: baseDistance * 2
}
}
// Array of tileNumbers in order of last moved
var history = [];
// Movement map
function movementMap(position) {
if (position == 9) return [6, 8];
if (position == 8) return [5, 7, 9];
if (position == 7) return [4, 8];
if (position == 6) return [3, 5, 9];
if (position == 5) return [2, 4, 6, 8];
if (position == 4) return [1, 5, 7];
if (position == 3) return [2, 6];
if (position == 2) return [1, 3, 5];
if (position == 1) return [2, 4];
}
// Board setup according to the tileMap
document.querySelector('#shuffle').addEventListener('click', shuffle, true);
document.querySelector('#solve').addEventListener('click', solve, true);
var tiles = document.querySelectorAll('.tile');
var delay = -50;
for (var i = 0; i < tiles.length; i++) {
tiles[i].addEventListener('click', tileClicked, true);
var tileId = tiles[i].innerHTML;
delay += 50;
setTimeout(setup, delay, tiles[i]);
}
function setup(tile) {
var tileId = tile.innerHTML;
// tile.style.left = tileMap[tileId].left + '%';
// tile.style.top = tileMap[tileId].top + '%';
var xMovement = parentX * (tileMap[tileId].left / 100);
var yMovement = parentX * (tileMap[tileId].top / 100);
var translateString = "translateX(" + xMovement + "px) " + "translateY(" + yMovement + "px)"
tile.style.webkitTransform = translateString;
recolorTile(tile, tileId);
}
function tileClicked(event) {
var tileNumber = event.target.innerHTML;
moveTile(event.target);
if (checkSolution()) {
console.log("You win!");
}
}
// Moves tile to empty spot
// Returns error message if tile cannot be moved
function moveTile(tile, recordHistory = true) {
// Check if Tile can be moved
// (must be touching empty tile)
// (must be directly perpendicular to empty tile)
var tileNumber = tile.innerHTML;
if (!tileMovable(tileNumber)) {
console.log("Tile " + tileNumber + " can't be moved.");
return;
}
// Push to history
if (recordHistory == true) {
if (history.length >= 3) {
if (history[history.length - 1] != history[history.length - 3]) history.push(tileNumber);
} else {
history.push(tileNumber);
}
}
// Swap tile with empty tile
var emptyTop = tileMap.empty.top;
var emptyLeft = tileMap.empty.left;
var emptyPosition = tileMap.empty.position;
tileMap.empty.top = tileMap[tileNumber].top;
tileMap.empty.left = tileMap[tileNumber].left;
tileMap.empty.position = tileMap[tileNumber].position;
// tile.style.top = emptyTop + '%';
// tile.style.left = emptyLeft + '%';
var xMovement = parentX * (emptyLeft / 100);
var yMovement = parentX * (emptyTop / 100);
var translateString = "translateX(" + xMovement + "px) " + "translateY(" + yMovement + "px)"
tile.style.webkitTransform = translateString;
tileMap[tileNumber].top = emptyTop;
tileMap[tileNumber].left = emptyLeft;
tileMap[tileNumber].position = emptyPosition;
recolorTile(tile, tileNumber);
}
// Determines whether a given tile can be moved
function tileMovable(tileNumber) {
var selectedTile = tileMap[tileNumber];
var emptyTile = tileMap.empty;
var movableTiles = movementMap(emptyTile.position);
if (movableTiles.includes(selectedTile.position)) {
return true;
} else {
return false;
}
}
// Returns true/false based on if the puzzle has been solved
function checkSolution() {
if (tileMap.empty.position !== 9) return false;
for (var key in tileMap) {
if ((key != 1) && (key != "empty")) {
if (tileMap[key].position < tileMap[key - 1].position) return false;
}
}
// Clear history if solved
history = [];
return true;
}
// Check if tile is in correct place!
function recolorTile(tile, tileId) {
if (tileId == tileMap[tileId].position) {
tile.classList.remove("error");
} else {
tile.classList.add("error");
}
}
// Shuffles the current tiles
shuffleTimeouts = [];
function shuffle() {
clearTimers(solveTimeouts);
var boardTiles = document.querySelectorAll('.tile');
var shuffleDelay = 200;
shuffleLoop();
var shuffleCounter = 0;
while (shuffleCounter < 20) {
shuffleDelay += 200;
shuffleTimeouts.push(setTimeout(shuffleLoop, shuffleDelay));
shuffleCounter++;
}
}
var lastShuffled;
function shuffleLoop() {
var emptyPosition = tileMap.empty.position;
var shuffleTiles = movementMap(emptyPosition);
var tilePosition = shuffleTiles[Math.floor(Math.floor(Math.random() * shuffleTiles.length))];
var locatedTile;
for (var i = 1; i <= 8; i++) {
if (tileMap[i].position == tilePosition) {
var locatedTileNumber = tileMap[i].tileNumber;
locatedTile = tiles[locatedTileNumber - 1];
}
}
if (lastShuffled != locatedTileNumber) {
moveTile(locatedTile);
lastShuffled = locatedTileNumber;
} else {
shuffleLoop();
}
}
function clearTimers(timeoutArray) {
for (var i = 0; i < timeoutArray.length; i++) {
clearTimeout(timeoutArray[i])
}
}
// Temporary function for solving puzzle.
// To be reimplemented with a more sophisticated algorithm
solveTimeouts = []
function solve() {
clearTimers(shuffleTimeouts);
repeater = history.length;
for (var i = 0; i < repeater; i++) {
console.log("started");
solveTimeouts.push(setTimeout(moveTile, i * 100, tiles[history.pop() - 1], false));
}
}
}
body {
font-family: 'Roboto Condensed', sans-serif;
font-weight: 700;
font-size: 24px;
background-color: #ECF0F1;
-webkit-tap-highlight-color: transparent;
khtml-tap-highlight-color: transparent;
}
.sliding-puzzle-figure {
margin: auto;
height: 360px;
width: 360px;
padding-bottom: 50vh;
padding-top: 10vh;
}
.sliding-puzzle-figure a {
cursor: pointer;
}
.sliding-puzzle-figure a#shuffle {
color: #E74C3C;
}
.sliding-puzzle-figure a#solve {
color: #3498DB;
}
.sliding-puzzle-figure .sliding-puzzle {
list-style-type: none;
position: relative;
margin-left: 0;
margin-right: 00;
width: 360px;
height: 360px;
box-sizing: border-box;
background-clip: border-box;
/* Firefox 4, Safari 5, Opera 10, IE 9 */
border: 18px solid #2C3E50;
border-radius: 10px;
background-color: #2C3E50;
}
.sliding-puzzle-figure .sliding-puzzle .tile {
position: absolute;
background: url(https://simonwiddowson.typepad.com/files/countryside360x360.jpg);
border-radius: 2px;
cursor: pointer;
width: 120px;
height: 120px;
display: flex;
justify-content: center;
align-items: center;
font-size: 0px;
left: 0%;
top: 0%;
transition: all 0.5s linear;
transition-timing-function: ease;
box-sizing: border-box;
}
.sliding-puzzle-figure .sliding-puzzle .tile.error {
background-color: #F0867D;
}
#tile1 {
background-position: left top;
}
#tile2 {
background-position: center top;
}
#tile3 {
background-position: right top;
}
#tile4 {
background-position: left center;
}
#tile5 {
background-position: center center;
}
#tile6 {
background-position: right center;
}
#tile7 {
background-position: left bottom;
}
#tile8 {
background-position: center bottom;
}
#media only screen and (max-width: 650px) {
.sliding-puzzle-figure {
width: 90vw;
height: 90vw;
max-height: 100vh;
}
.sliding-puzzle-figure .sliding-puzzle {
border-width: 10px;
border-radius: 14px;
}
.sliding-puzzle-figure .tile {
font-size: 1em;
}
}
/*# sourceMappingURL=style.css.map */
<link href="https://fonts.googleapis.com/css?family=Roboto+Condensed:400,700" rel="stylesheet">
<figure class="sliding-puzzle-figure">
<div class="sliding-puzzle">
<div class="tile" id="tile1">1</div>
<div class="tile" id="tile2">2</div>
<div class="tile" id="tile3">3</div>
<div class="tile" id="tile4">4</div>
<div class="tile" id="tile5">5</div>
<div class="tile" id="tile6">6</div>
<div class="tile" id="tile7">7</div>
<div class="tile" id="tile8">8</div>
</div>
<figcaption><br><br> Barry Paling | <a id="shuffle">Shuffle</a> | <a id="solve">Wie ben ik</a>
</figcaption>
</figure>
You call game on load, remove the shuffle click and just call shuffle() at the end of game() :
window.addEventListener("load",game);
function game() {
...
shuffle();
}
window.addEventListener("load",game);
function game() {
// Data structure to hold positions of tiles
var parentX = document.querySelector(".sliding-puzzle").clientHeight;
var baseDistance = 38;
var tileMap = {
1: {
tileNumber: 1,
position: 1,
top: 0,
left: 0
},
2: {
tileNumber: 2,
position: 2,
top: 0,
left: baseDistance * 1
},
3: {
tileNumber: 3,
position: 3,
top: 0,
left: baseDistance * 2
},
4: {
tileNumber: 4,
position: 4,
top: baseDistance,
left: 0
},
5: {
tileNumber: 5,
position: 5,
top: baseDistance,
left: baseDistance
},
6: {
tileNumber: 6,
position: 6,
top: baseDistance,
left: baseDistance * 2
},
7: {
tileNumber: 7,
position: 7,
top: baseDistance * 2,
left: 0
},
8: {
tileNumber: 8,
position: 8,
top: baseDistance * 2,
left: baseDistance
},
empty: {
position: 9,
top: baseDistance * 2,
left: baseDistance * 2
}
}
// Array of tileNumbers in order of last moved
var history = [];
// Movement map
function movementMap(position) {
if (position == 9) return [6, 8];
if (position == 8) return [5, 7, 9];
if (position == 7) return [4, 8];
if (position == 6) return [3, 5, 9];
if (position == 5) return [2, 4, 6, 8];
if (position == 4) return [1, 5, 7];
if (position == 3) return [2, 6];
if (position == 2) return [1, 3, 5];
if (position == 1) return [2, 4];
}
// Board setup according to the tileMap
document.querySelector('#solve').addEventListener('click', solve, true);
var tiles = document.querySelectorAll('.tile');
var delay = -50;
for (var i = 0; i < tiles.length; i++) {
tiles[i].addEventListener('click', tileClicked, true);
var tileId = tiles[i].innerHTML;
delay += 50;
setTimeout(setup, delay, tiles[i]);
}
function setup(tile) {
var tileId = tile.innerHTML;
// tile.style.left = tileMap[tileId].left + '%';
// tile.style.top = tileMap[tileId].top + '%';
var xMovement = parentX * (tileMap[tileId].left / 100);
var yMovement = parentX * (tileMap[tileId].top / 100);
var translateString = "translateX(" + xMovement + "px) " + "translateY(" + yMovement + "px)"
tile.style.webkitTransform = translateString;
recolorTile(tile, tileId);
}
function tileClicked(event) {
var tileNumber = event.target.innerHTML;
moveTile(event.target);
if (checkSolution()) {
console.log("You win!");
}
}
// Moves tile to empty spot
// Returns error message if tile cannot be moved
function moveTile(tile, recordHistory = true) {
// Check if Tile can be moved
// (must be touching empty tile)
// (must be directly perpendicular to empty tile)
var tileNumber = tile.innerHTML;
if (!tileMovable(tileNumber)) {
console.log("Tile " + tileNumber + " can't be moved.");
return;
}
// Push to history
if (recordHistory == true) {
if (history.length >= 3) {
if (history[history.length - 1] != history[history.length - 3]) history.push(tileNumber);
} else {
history.push(tileNumber);
}
}
// Swap tile with empty tile
var emptyTop = tileMap.empty.top;
var emptyLeft = tileMap.empty.left;
var emptyPosition = tileMap.empty.position;
tileMap.empty.top = tileMap[tileNumber].top;
tileMap.empty.left = tileMap[tileNumber].left;
tileMap.empty.position = tileMap[tileNumber].position;
// tile.style.top = emptyTop + '%';
// tile.style.left = emptyLeft + '%';
var xMovement = parentX * (emptyLeft / 100);
var yMovement = parentX * (emptyTop / 100);
var translateString = "translateX(" + xMovement + "px) " + "translateY(" + yMovement + "px)"
tile.style.webkitTransform = translateString;
tileMap[tileNumber].top = emptyTop;
tileMap[tileNumber].left = emptyLeft;
tileMap[tileNumber].position = emptyPosition;
recolorTile(tile, tileNumber);
}
// Determines whether a given tile can be moved
function tileMovable(tileNumber) {
var selectedTile = tileMap[tileNumber];
var emptyTile = tileMap.empty;
var movableTiles = movementMap(emptyTile.position);
if (movableTiles.includes(selectedTile.position)) {
return true;
} else {
return false;
}
}
// Returns true/false based on if the puzzle has been solved
function checkSolution() {
if (tileMap.empty.position !== 9) return false;
for (var key in tileMap) {
if ((key != 1) && (key != "empty")) {
if (tileMap[key].position < tileMap[key - 1].position) return false;
}
}
// Clear history if solved
history = [];
return true;
}
// Check if tile is in correct place!
function recolorTile(tile, tileId) {
if (tileId == tileMap[tileId].position) {
tile.classList.remove("error");
} else {
tile.classList.add("error");
}
}
// Shuffles the current tiles
shuffleTimeouts = [];
function shuffle() {
clearTimers(solveTimeouts);
var boardTiles = document.querySelectorAll('.tile');
var shuffleDelay = 200;
shuffleLoop();
var shuffleCounter = 0;
while (shuffleCounter < 20) {
shuffleDelay += 200;
shuffleTimeouts.push(setTimeout(shuffleLoop, shuffleDelay));
shuffleCounter++;
}
}
var lastShuffled;
function shuffleLoop() {
var emptyPosition = tileMap.empty.position;
var shuffleTiles = movementMap(emptyPosition);
var tilePosition = shuffleTiles[Math.floor(Math.floor(Math.random() * shuffleTiles.length))];
var locatedTile;
for (var i = 1; i <= 8; i++) {
if (tileMap[i].position == tilePosition) {
var locatedTileNumber = tileMap[i].tileNumber;
locatedTile = tiles[locatedTileNumber - 1];
}
}
if (lastShuffled != locatedTileNumber) {
moveTile(locatedTile);
lastShuffled = locatedTileNumber;
} else {
shuffleLoop();
}
}
function clearTimers(timeoutArray) {
for (var i = 0; i < timeoutArray.length; i++) {
clearTimeout(timeoutArray[i])
}
}
// Temporary function for solving puzzle.
// To be reimplemented with a more sophisticated algorithm
solveTimeouts = []
function solve() {
clearTimers(shuffleTimeouts);
repeater = history.length;
for (var i = 0; i < repeater; i++) {
solveTimeouts.push(setTimeout(moveTile, i * 100, tiles[history.pop() - 1], false));
}
}
shuffle()
}
body {
font-family: 'Roboto Condensed', sans-serif;
font-weight: 700;
font-size: 24px;
background-color: #ECF0F1;
-webkit-tap-highlight-color: transparent;
khtml-tap-highlight-color: transparent;
}
.sliding-puzzle-figure {
margin: auto;
height: 360px;
width: 360px;
padding-bottom: 50vh;
padding-top: 10vh;
}
.sliding-puzzle-figure a {
cursor: pointer;
}
.sliding-puzzle-figure a#shuffle {
color: #E74C3C;
}
.sliding-puzzle-figure a#solve {
color: #3498DB;
}
.sliding-puzzle-figure .sliding-puzzle {
list-style-type: none;
position: relative;
margin-left: 0;
margin-right: 00;
width: 360px;
height: 360px;
box-sizing: border-box;
background-clip: border-box;
/* Firefox 4, Safari 5, Opera 10, IE 9 */
border: 18px solid #2C3E50;
border-radius: 10px;
background-color: #2C3E50;
}
.sliding-puzzle-figure .sliding-puzzle .tile {
position: absolute;
background: url(https://simonwiddowson.typepad.com/files/countryside360x360.jpg);
border-radius: 2px;
cursor: pointer;
width: 120px;
height: 120px;
display: flex;
justify-content: center;
align-items: center;
font-size: 0px;
left: 0%;
top: 0%;
transition: all 0.5s linear;
transition-timing-function: ease;
box-sizing: border-box;
}
.sliding-puzzle-figure .sliding-puzzle .tile.error {
background-color: #F0867D;
}
#tile1 {
background-position: left top;
}
#tile2 {
background-position: center top;
}
#tile3 {
background-position: right top;
}
#tile4 {
background-position: left center;
}
#tile5 {
background-position: center center;
}
#tile6 {
background-position: right center;
}
#tile7 {
background-position: left bottom;
}
#tile8 {
background-position: center bottom;
}
#media only screen and (max-width: 650px) {
.sliding-puzzle-figure {
width: 90vw;
height: 90vw;
max-height: 100vh;
}
.sliding-puzzle-figure .sliding-puzzle {
border-width: 10px;
border-radius: 14px;
}
.sliding-puzzle-figure .tile {
font-size: 1em;
}
}
/*# sourceMappingURL=style.css.map */
<link href="https://fonts.googleapis.com/css?family=Roboto+Condensed:400,700" rel="stylesheet">
<figure class="sliding-puzzle-figure">
<div class="sliding-puzzle">
<div class="tile" id="tile1">1</div>
<div class="tile" id="tile2">2</div>
<div class="tile" id="tile3">3</div>
<div class="tile" id="tile4">4</div>
<div class="tile" id="tile5">5</div>
<div class="tile" id="tile6">6</div>
<div class="tile" id="tile7">7</div>
<div class="tile" id="tile8">8</div>
</div>
<figcaption><br><br> Barry Paling | <a id="solve">Wie ben ik</a>
</figcaption>
</figure>
I would like to make the face of this snake an image. Currently, it using a fill style with a color but I would like it to be an image. How can I do it with this code?
In addition, I want to find out how to add arrows so that it could work on a mobile phone. Thank you for anyone who can help or provide insight.
(function() {
/////////////////////////////////////////////////////////////
// Canvas & Context
var canvas;
var ctx;
// Snake
var snake;
var snake_dir;
var snake_next_dir;
var snake_speed;
// Food
var food = {
x: 0,
y: 0
};
// Score
var score;
// Wall
var wall;
// HTML Elements
var screen_snake;
var screen_menu;
var screen_setting;
var screen_gameover;
var button_newgame_menu;
var button_newgame_setting;
var button_newgame_gameover;
var button_setting_menu;
var button_setting_gameover;
var ele_score;
var speed_setting;
var wall_setting;
/////////////////////////////////////////////////////////////
var activeDot = function(x, y) {
ctx.fillStyle = "#FFFFFF";
ctx.fillRect(x * 10, y * 10, 10, 10);
}
/////////////////////////////////////////////////////////////
var changeDir = function(key) {
if (key == 38 && snake_dir != 2) {
snake_next_dir = 0;
} else {
if (key == 39 && snake_dir != 3) {
snake_next_dir = 1;
} else {
if (key == 40 && snake_dir != 0) {
snake_next_dir = 2;
} else {
if (key == 37 && snake_dir != 1) {
snake_next_dir = 3;
}
}
}
}
}
/////////////////////////////////////////////////////////////
var addFood = function() {
food.x = Math.floor(Math.random() * ((canvas.width / 10) - 1));
food.y = Math.floor(Math.random() * ((canvas.height / 10) - 1));
for (var i = 0; i < snake.length; i++) {
if (checkBlock(food.x, food.y, snake[i].x, snake[i].y)) {
addFood();
}
}
}
/////////////////////////////////////////////////////////////
var checkBlock = function(x, y, _x, _y) {
return (x == _x && y == _y) ? true : false;
}
/////////////////////////////////////////////////////////////
var altScore = function(score_val) {
ele_score.innerHTML = String(score_val);
}
/////////////////////////////////////////////////////////////
var mainLoop = function() {
var _x = snake[0].x;
var _y = snake[0].y;
snake_dir = snake_next_dir;
// 0 - Up, 1 - Right, 2 - Down, 3 - Left
switch (snake_dir) {
case 0:
_y--;
break;
case 1:
_x++;
break;
case 2:
_y++;
break;
case 3:
_x--;
break;
}
snake.pop();
snake.unshift({
x: _x,
y: _y
});
// --------------------
// Wall
if (wall == 1) {
// On
if (snake[0].x < 0 || snake[0].x == canvas.width / 10 || snake[0].y < 0 || snake[0].y == canvas.height / 10) {
showScreen(3);
return;
}
} else {
// Off
for (var i = 0, x = snake.length; i < x; i++) {
if (snake[i].x < 0) {
snake[i].x = snake[i].x + (canvas.width / 10);
}
if (snake[i].x == canvas.width / 10) {
snake[i].x = snake[i].x - (canvas.width / 10);
}
if (snake[i].y < 0) {
snake[i].y = snake[i].y + (canvas.height / 10);
}
if (snake[i].y == canvas.height / 10) {
snake[i].y = snake[i].y - (canvas.height / 10);
}
}
}
// --------------------
// Autophagy death
for (var i = 1; i < snake.length; i++) {
if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) {
showScreen(3);
return;
}
}
// --------------------
// Eat Food
if (checkBlock(snake[0].x, snake[0].y, food.x, food.y)) {
snake[snake.length] = {
x: snake[0].x,
y: snake[0].y
};
score += 1;
altScore(score);
addFood();
activeDot(food.x, food.y);
}
// --------------------
ctx.beginPath();
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// --------------------
for (var i = 0; i < snake.length; i++) {
activeDot(snake[i].x, snake[i].y);
}
// --------------------
activeDot(food.x, food.y);
// Debug
//document.getElementById("debug").innerHTML = snake_dir + " " + snake_next_dir + " " + snake[0].x + " " + snake[0].y;
setTimeout(mainLoop, snake_speed);
}
/////////////////////////////////////////////////////////////
var newGame = function() {
showScreen(0);
screen_snake.focus();
snake = [];
for (var i = 4; i >= 0; i--) {
snake.push({
x: i,
y: 15
});
}
snake_next_dir = 1;
score = 0;
altScore(score);
addFood();
canvas.onkeydown = function(evt) {
evt = evt || window.event;
changeDir(evt.keyCode);
}
mainLoop();
}
/////////////////////////////////////////////////////////////
// Change the snake speed...
// 150 = slow
// 100 = normal
// 50 = fast
var setSnakeSpeed = function(speed_value) {
snake_speed = speed_value;
}
/////////////////////////////////////////////////////////////
var setWall = function(wall_value) {
wall = wall_value;
if (wall == 0) {
screen_snake.style.borderColor = "#606060";
}
if (wall == 1) {
screen_snake.style.borderColor = "#FFFFFF";
}
}
/////////////////////////////////////////////////////////////
// 0 for the game
// 1 for the main menu
// 2 for the settings screen
// 3 for the game over screen
var showScreen = function(screen_opt) {
switch (screen_opt) {
case 0:
screen_snake.style.display = "block";
screen_menu.style.display = "none";
screen_setting.style.display = "none";
screen_gameover.style.display = "none";
break;
case 1:
screen_snake.style.display = "none";
screen_menu.style.display = "block";
screen_setting.style.display = "none";
screen_gameover.style.display = "none";
break;
case 2:
screen_snake.style.display = "none";
screen_menu.style.display = "none";
screen_setting.style.display = "block";
screen_gameover.style.display = "none";
break;
case 3:
screen_snake.style.display = "none";
screen_menu.style.display = "none";
screen_setting.style.display = "none";
screen_gameover.style.display = "block";
break;
}
}
/////////////////////////////////////////////////////////////
window.onload = function() {
canvas = document.getElementById("snake");
ctx = canvas.getContext("2d");
// Screens
screen_snake = document.getElementById("snake");
screen_menu = document.getElementById("menu");
screen_gameover = document.getElementById("gameover");
screen_setting = document.getElementById("setting");
// Buttons
button_newgame_menu = document.getElementById("newgame_menu");
button_newgame_setting = document.getElementById("newgame_setting");
button_newgame_gameover = document.getElementById("newgame_gameover");
button_setting_menu = document.getElementById("setting_menu");
button_setting_gameover = document.getElementById("setting_gameover");
// etc
ele_score = document.getElementById("score_value");
speed_setting = document.getElementsByName("speed");
wall_setting = document.getElementsByName("wall");
// --------------------
button_newgame_menu.onclick = function() {
newGame();
};
button_newgame_gameover.onclick = function() {
newGame();
};
button_newgame_setting.onclick = function() {
newGame();
};
button_setting_menu.onclick = function() {
showScreen(2);
};
button_setting_gameover.onclick = function() {
showScreen(2)
};
setSnakeSpeed(150);
setWall(1);
showScreen("menu");
// --------------------
// Settings
// speed
for (var i = 0; i < speed_setting.length; i++) {
speed_setting[i].addEventListener("click", function() {
for (var i = 0; i < speed_setting.length; i++) {
if (speed_setting[i].checked) {
setSnakeSpeed(speed_setting[i].value);
}
}
});
}
// wall
for (var i = 0; i < wall_setting.length; i++) {
wall_setting[i].addEventListener("click", function() {
for (var i = 0; i < wall_setting.length; i++) {
if (wall_setting[i].checked) {
setWall(wall_setting[i].value);
}
}
});
}
document.onkeydown = function(evt) {
if (screen_gameover.style.display == "block") {
evt = evt || window.event;
if (evt.keyCode == 32) {
newGame();
}
}
}
}
})();
::selection {
color: #FFFFFF;
background: transparent;
}
::-moz-selection {
color: #FFFFFF;
background: transparent;
}
* {
margin: 0;
padding: 0;
font-family: "VT323";
}
body {
background-color: #000000;
}
.wrap {
margin-left: auto;
margin-right: auto;
}
header {
width: 340px;
font-size: 0;
}
canvas {
display: none;
border-style: solid;
border-width: 10px;
border-color: #FFFFFF;
}
canvas:focus {
outline: none;
}
/* Top Styles */
h1 {
display: inline-block;
width: 100px;
font-size: 32px;
color: #FFFFFF;
}
.score {
display: inline-block;
width: 240px;
font-size: 20px;
color: #FFFFFF;
text-align: right;
}
.score_value {
font-size: inherit;
}
/* All screens style */
#gameover a,
#setting a,
#menu a {
display: block;
}
#gameover a,
#setting a:hover,
#menu a:hover {
cursor: pointer;
}
#gameover a:hover::before,
#setting a:hover::before,
#menu a:hover::before {
content: ">";
margin-right: 10px;
}
/* Menu Screen Style */
#menu {
display: block;
width: 340px;
padding-top: 95px;
padding-bottom: 95px;
font-size: 40px;
margin-left: auto;
margin-right: auto;
text-align: center;
color: #FFF;
}
#menu h2 {
-webkit-animation: logo-ani 1000ms linear infinite;
animation: logo-ani 1000ms linear infinite;
margin-bottom: 30px;
}
#menu a {
font-size: 30px;
}
#-webkit-keyframes logo-ani {
50% {
-webkit-transform: scale(1.3, 1.3);
}
100% {
-webkit-transform: scale(1.0, 1.0);
}
}
#keyframes logo-ani {
50% {
transform: scale(1.3, 1.3);
}
100% {
transform: scale(1.0, 1.0);
}
}
/* Game Over Screen Style */
#gameover {
display: none;
width: 340px;
padding-top: 95px;
padding-bottom: 95px;
margin-left: auto;
margin-right: auto;
text-align: center;
font-size: 30px;
color: #FFF;
}
#gameover p {
margin-top: 25px;
font-size: 20px;
}
/* Settings Screen Style */
#setting {
display: none;
width: 340px;
margin-left: auto;
margin-right: auto;
padding-top: 85px;
padding-bottom: 85px;
font-size: 30px;
color: #FFF;
text-align: center;
}
#setting h2 {
margin-bottom: 15px;
}
#setting p {
margin-top: 10px;
}
#setting input {
display: none;
}
#setting label {
cursor: pointer;
}
#setting input:checked+label {
background-color: #FFF;
color: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet">
<header class="wrap">
<h1>Snake</h1>
<p class="score">Score: <span id="score_value">0</span></p>
</header>
<canvas class="wrap" id="snake" width="320" height="320" tabindex="1"></canvas>
<!-- Game Over Screen -->
<div id="gameover">
<h2>Game Over</h2>
<p>press <span style="background-color: #FFFFFF; color: #000000">space</span> to begin a</p>
<a id="newgame_gameover">new game</a>
<a id="setting_gameover">settings</a>
</div>
<!-- Setting screen -->
<div id="setting">
<h2>Settings</h2>
<a id="newgame_setting">new game</a>
<p>Speed:
<input id="speed1" type="radio" name="speed" value="120" checked/>
<label for="speed1">Slow</label>
<input id="speed2" type="radio" name="speed" value="75" />
<label for="speed2">Normal</label>
<input id="speed3" type="radio" name="speed" value="35" />
<label for="speed3">Fast</label>
</p>
<p>Wall:
<input id="wallon" type="radio" name="wall" value="1" checked/>
<label for="wallon">On</label>
<input id="walloff" type="radio" name="wall" value="0" />
<label for="walloff">Off</label>
</p>
</div>
<!-- Main Menu Screen -->
<div id="menu">
<h2>Snake</h2>
<a id="newgame_menu">new game</a>
<a id="setting_menu">settings</a>
</div>
Convert your image to a base64 string, then for the first dot/head use the image instead:
var img = new Image(); // Create new img element
img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAA21BMVEXoTTvqTTzpUD7nTjzmTTvrUkDpTjzmSznUPSrVPCrnTDrSOSfTOizlTDrvoJnoqJ/pVkTmU0P5urH1p53mSTjjPiv7nZF0f4NZaGvpn5b9s6qDl5iira/+nI/jPirkPyz7lop+iYtleHzsnpT7raOTpqqqtbf4lovnSjf0lorylonpSDbnTDj5oZf2mIzlSDXkOyjIY1vrd2rrWEbZZ1zYQDLsTTroTz/lRjPalY7+///////j19fVSz7tTjvmTjntRjPialzZhH3sVkXsSzniRTTYQTDmSTbmTT3qb45jAAAAAWJLR0Q7OQ70bAAAAAd0SU1FB+QEDw4AEYzMSFoAAABvSURBVAjXY2BgZGJiZmFlZWNgYGDn4GTn4ubhBTJZ+PgFBIWERdgYRMXEJSSlpGVk5RjkFRSVlFVU1dTlGRg0NLW0dXT19JkZGNgMDI2MTUzNgNrMLSytrG1s7eyBbGYHRydnFwYwYHN1c/dgYAAAtKsKjnGo4BwAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjAtMDQtMTVUMTQ6MDA6MTctMDQ6MDAVi7z/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIwLTA0LTE1VDE0OjAwOjE3LTA0OjAwZNYEQwAAAABJRU5ErkJggg==";
for (var i = 0; i < snake.length; i++) {
if (!i) {
ctx.drawImage(img, snake[i].x * 10, snake[i].y * 10);
} else {
activeDot(snake[i].x, snake[i].y);
}
}
I want to apply magnifying glass and lightbox both on an image so when a user hovers on an image the glass effect will appear and when he clicks the lightbox will appear. But in my case the lightbox stopped working when I actived the magnifying glass. So when I remove the glass CSS lightbox worked normally.
/*!
* Lightbox v2.10.0
* by Lokesh Dhakar
*
* More info:
* http://lokeshdhakar.com/projects/lightbox2/
*
* Copyright 2007, 2018 Lokesh Dhakar
* Released under the MIT license
* https://github.com/lokesh/lightbox2/blob/master/LICENSE
*
* #preserve
*/
! function(a, b) {
"function" == typeof define && define.amd ? define(["jquery"], b) : "object" == typeof exports ? module.exports = b(require("jquery")) : a.lightbox = b(a.jQuery)
}(this, function(a) {
function b(b) {
this.album = [], this.currentImageIndex = void 0, this.init(), this.options = a.extend({}, this.constructor.defaults), this.option(b)
}
return b.defaults = {
albumLabel: "Image %1 of %2",
alwaysShowNavOnTouchDevices: !1,
fadeDuration: 600,
fitImagesInViewport: !0,
imageFadeDuration: 600,
positionFromTop: 50,
resizeDuration: 700,
showImageNumberLabel: !0,
wrapAround: !1,
disableScrolling: !1,
sanitizeTitle: !1
}, b.prototype.option = function(b) {
a.extend(this.options, b)
}, b.prototype.imageCountLabel = function(a, b) {
return this.options.albumLabel.replace(/%1/g, a).replace(/%2/g, b)
}, b.prototype.init = function() {
var b = this;
a(document).ready(function() {
b.enable(), b.build()
})
}, b.prototype.enable = function() {
var b = this;
a("body").on("click", "a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]", function(c) {
return b.start(a(c.currentTarget)), !1
})
}, b.prototype.build = function() {
if (!(a("#lightbox").length > 0)) {
var b = this;
a('<div id="lightboxOverlay" class="lightboxOverlay"></div><div id="lightbox" class="lightbox"><div class="lb-outerContainer"><div class="lb-container"><img class="lb-image" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" /><div class="lb-nav"><a class="lb-prev" href="" ></a><a class="lb-next" href="" ></a></div><div class="lb-loader"><a class="lb-cancel"></a></div></div></div><div class="lb-dataContainer"><div class="lb-data"><div class="lb-details"><span class="lb-caption"></span><span class="lb-number"></span></div><div class="lb-closeContainer"><a class="lb-close"></a></div></div></div></div>').appendTo(a("body")), this.$lightbox = a("#lightbox"), this.$overlay = a("#lightboxOverlay"), this.$outerContainer = this.$lightbox.find(".lb-outerContainer"), this.$container = this.$lightbox.find(".lb-container"), this.$image = this.$lightbox.find(".lb-image"), this.$nav = this.$lightbox.find(".lb-nav"), this.containerPadding = {
top: parseInt(this.$container.css("padding-top"), 10),
right: parseInt(this.$container.css("padding-right"), 10),
bottom: parseInt(this.$container.css("padding-bottom"), 10),
left: parseInt(this.$container.css("padding-left"), 10)
}, this.imageBorderWidth = {
top: parseInt(this.$image.css("border-top-width"), 10),
right: parseInt(this.$image.css("border-right-width"), 10),
bottom: parseInt(this.$image.css("border-bottom-width"), 10),
left: parseInt(this.$image.css("border-left-width"), 10)
}, this.$overlay.hide().on("click", function() {
return b.end(), !1
}), this.$lightbox.hide().on("click", function(c) {
return "lightbox" === a(c.target).attr("id") && b.end(), !1
}), this.$outerContainer.on("click", function(c) {
return "lightbox" === a(c.target).attr("id") && b.end(), !1
}), this.$lightbox.find(".lb-prev").on("click", function() {
return 0 === b.currentImageIndex ? b.changeImage(b.album.length - 1) : b.changeImage(b.currentImageIndex - 1), !1
}), this.$lightbox.find(".lb-next").on("click", function() {
return b.currentImageIndex === b.album.length - 1 ? b.changeImage(0) : b.changeImage(b.currentImageIndex + 1), !1
}), this.$nav.on("mousedown", function(a) {
3 === a.which && (b.$nav.css("pointer-events", "none"), b.$lightbox.one("contextmenu", function() {
setTimeout(function() {
this.$nav.css("pointer-events", "auto")
}.bind(b), 0)
}))
}), this.$lightbox.find(".lb-loader, .lb-close").on("click", function() {
return b.end(), !1
})
}
}, b.prototype.start = function(b) {
function c(a) {
d.album.push({
alt: a.attr("data-alt"),
link: a.attr("href"),
title: a.attr("data-title") || a.attr("title")
})
}
var d = this,
e = a(window);
e.on("resize", a.proxy(this.sizeOverlay, this)), a("select, object, embed").css({
visibility: "hidden"
}), this.sizeOverlay(), this.album = [];
var f, g = 0,
h = b.attr("data-lightbox");
if (h) {
f = a(b.prop("tagName") + '[data-lightbox="' + h + '"]');
for (var i = 0; i < f.length; i = ++i) c(a(f[i])), f[i] === b[0] && (g = i)
} else if ("lightbox" === b.attr("rel")) c(b);
else {
f = a(b.prop("tagName") + '[rel="' + b.attr("rel") + '"]');
for (var j = 0; j < f.length; j = ++j) c(a(f[j])), f[j] === b[0] && (g = j)
}
var k = e.scrollTop() + this.options.positionFromTop,
l = e.scrollLeft();
this.$lightbox.css({
top: k + "px",
left: l + "px"
}).fadeIn(this.options.fadeDuration), this.options.disableScrolling && a("html").addClass("lb-disable-scrolling"), this.changeImage(g)
}, b.prototype.changeImage = function(b) {
var c = this;
this.disableKeyboardNav();
var d = this.$lightbox.find(".lb-image");
this.$overlay.fadeIn(this.options.fadeDuration), a(".lb-loader").fadeIn("slow"), this.$lightbox.find(".lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption").hide(), this.$outerContainer.addClass("animating");
var e = new Image;
e.onload = function() {
var f, g, h, i, j, k;
d.attr({
alt: c.album[b].alt,
src: c.album[b].link
}), a(e), d.width(e.width), d.height(e.height), c.options.fitImagesInViewport && (k = a(window).width(), j = a(window).height(), i = k - c.containerPadding.left - c.containerPadding.right - c.imageBorderWidth.left - c.imageBorderWidth.right - 20, h = j - c.containerPadding.top - c.containerPadding.bottom - c.imageBorderWidth.top - c.imageBorderWidth.bottom - 120, c.options.maxWidth && c.options.maxWidth < i && (i = c.options.maxWidth), c.options.maxHeight && c.options.maxHeight < i && (h = c.options.maxHeight), (e.width > i || e.height > h) && (e.width / i > e.height / h ? (g = i, f = parseInt(e.height / (e.width / g), 10), d.width(g), d.height(f)) : (f = h, g = parseInt(e.width / (e.height / f), 10), d.width(g), d.height(f)))), c.sizeContainer(d.width(), d.height())
}, e.src = this.album[b].link, this.currentImageIndex = b
}, b.prototype.sizeOverlay = function() {
this.$overlay.width(a(document).width()).height(a(document).height())
}, b.prototype.sizeContainer = function(a, b) {
function c() {
d.$lightbox.find(".lb-dataContainer").width(g), d.$lightbox.find(".lb-prevLink").height(h), d.$lightbox.find(".lb-nextLink").height(h), d.showImage()
}
var d = this,
e = this.$outerContainer.outerWidth(),
f = this.$outerContainer.outerHeight(),
g = a + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right,
h = b + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom;
e !== g || f !== h ? this.$outerContainer.animate({
width: g,
height: h
}, this.options.resizeDuration, "swing", function() {
c()
}) : c()
}, b.prototype.showImage = function() {
this.$lightbox.find(".lb-loader").stop(!0).hide(), this.$lightbox.find(".lb-image").fadeIn(this.options.imageFadeDuration), this.updateNav(), this.updateDetails(), this.preloadNeighboringImages(), this.enableKeyboardNav()
}, b.prototype.updateNav = function() {
var a = !1;
try {
document.createEvent("TouchEvent"), a = !!this.options.alwaysShowNavOnTouchDevices
} catch (a) {}
this.$lightbox.find(".lb-nav").show(), this.album.length > 1 && (this.options.wrapAround ? (a && this.$lightbox.find(".lb-prev, .lb-next").css("opacity", "1"), this.$lightbox.find(".lb-prev, .lb-next").show()) : (this.currentImageIndex > 0 && (this.$lightbox.find(".lb-prev").show(), a && this.$lightbox.find(".lb-prev").css("opacity", "1")), this.currentImageIndex < this.album.length - 1 && (this.$lightbox.find(".lb-next").show(), a && this.$lightbox.find(".lb-next").css("opacity", "1"))))
}, b.prototype.updateDetails = function() {
var b = this;
if (void 0 !== this.album[this.currentImageIndex].title && "" !== this.album[this.currentImageIndex].title) {
var c = this.$lightbox.find(".lb-caption");
this.options.sanitizeTitle ? c.text(this.album[this.currentImageIndex].title) : c.html(this.album[this.currentImageIndex].title), c.fadeIn("fast").find("a").on("click", function(b) {
void 0 !== a(this).attr("target") ? window.open(a(this).attr("href"), a(this).attr("target")) : location.href = a(this).attr("href")
})
}
if (this.album.length > 1 && this.options.showImageNumberLabel) {
var d = this.imageCountLabel(this.currentImageIndex + 1, this.album.length);
this.$lightbox.find(".lb-number").text(d).fadeIn("fast")
} else this.$lightbox.find(".lb-number").hide();
this.$outerContainer.removeClass("animating"), this.$lightbox.find(".lb-dataContainer").fadeIn(this.options.resizeDuration, function() {
return b.sizeOverlay()
})
}, b.prototype.preloadNeighboringImages = function() {
if (this.album.length > this.currentImageIndex + 1) {
(new Image).src = this.album[this.currentImageIndex + 1].link
}
if (this.currentImageIndex > 0) {
(new Image).src = this.album[this.currentImageIndex - 1].link
}
}, b.prototype.enableKeyboardNav = function() {
a(document).on("keyup.keyboard", a.proxy(this.keyboardAction, this))
}, b.prototype.disableKeyboardNav = function() {
a(document).off(".keyboard")
}, b.prototype.keyboardAction = function(a) {
var b = a.keyCode,
c = String.fromCharCode(b).toLowerCase();
27 === b || c.match(/x|o|c/) ? this.end() : "p" === c || 37 === b ? 0 !== this.currentImageIndex ? this.changeImage(this.currentImageIndex - 1) : this.options.wrapAround && this.album.length > 1 && this.changeImage(this.album.length - 1) : "n" !== c && 39 !== b || (this.currentImageIndex !== this.album.length - 1 ? this.changeImage(this.currentImageIndex + 1) : this.options.wrapAround && this.album.length > 1 && this.changeImage(0))
}, b.prototype.end = function() {
this.disableKeyboardNav(), a(window).off("resize", this.sizeOverlay), this.$lightbox.fadeOut(this.options.fadeDuration), this.$overlay.fadeOut(this.options.fadeDuration), a("select, object, embed").css({
visibility: "visible"
}), this.options.disableScrolling && a("html").removeClass("lb-disable-scrolling")
}, new b
});
//# sourceMappingURL=lightbox.min.map
//magnyfing glass
$(function() {
var native_width = 0;
var native_height = 0;
var mouse = {
x: 0,
y: 0
};
var magnify;
var cur_img;
var ui = {
magniflier: $('.magniflier')
};
// Add the magnifying glass
if (ui.magniflier.length) {
var div = document.createElement('div');
div.setAttribute('class', 'glass');
ui.glass = $(div);
$('body').append(div);
}
// All the magnifying will happen on "mousemove"
var mouseMove = function(e) {
var $el = $(this);
// Container offset relative to document
var magnify_offset = cur_img.offset();
// Mouse position relative to container
// pageX/pageY - container's offsetLeft/offetTop
mouse.x = e.pageX - magnify_offset.left;
mouse.y = e.pageY - magnify_offset.top;
// The Magnifying glass should only show up when the mouse is inside
// It is important to note that attaching mouseout and then hiding
// the glass wont work cuz mouse will never be out due to the glass
// being inside the parent and having a higher z-index (positioned above)
if (
mouse.x < cur_img.width() &&
mouse.y < cur_img.height() &&
mouse.x > 0 &&
mouse.y > 0
) {
magnify(e);
} else {
ui.glass.fadeOut(100);
}
return;
};
var magnify = function(e) {
// The background position of div.glass will be
// changed according to the position
// of the mouse over the img.magniflier
//
// So we will get the ratio of the pixel
// under the mouse with respect
// to the image and use that to position the
// large image inside the magnifying glass
var rx = Math.round(mouse.x / cur_img.width() * native_width - ui.glass.width() / 2) * -1;
var ry = Math.round(mouse.y / cur_img.height() * native_height - ui.glass.height() / 2) * -1;
var bg_pos = rx + "px " + ry + "px";
// Calculate pos for magnifying glass
//
// Easy Logic: Deduct half of width/height
// from mouse pos.
// var glass_left = mouse.x - ui.glass.width() / 2;
// var glass_top = mouse.y - ui.glass.height() / 2;
var glass_left = e.pageX - ui.glass.width() / 2;
var glass_top = e.pageY - ui.glass.height() / 2;
//console.log(glass_left, glass_top, bg_pos)
// Now, if you hover on the image, you should
// see the magnifying glass in action
ui.glass.css({
left: glass_left,
top: glass_top,
backgroundPosition: bg_pos
});
return;
};
$('.magniflier').on('mousemove', function() {
ui.glass.fadeIn(200);
cur_img = $(this);
var large_img_loaded = cur_img.data('large-img-loaded');
var src = cur_img.data('large') || cur_img.attr('src');
// Set large-img-loaded to true
// cur_img.data('large-img-loaded', true)
if (src) {
ui.glass.css({
'background-image': 'url(' + src + ')',
'background-repeat': 'no-repeat'
});
}
// When the user hovers on the image, the script will first calculate
// the native dimensions if they don't exist. Only after the native dimensions
// are available, the script will show the zoomed version.
//if(!native_width && !native_height) {
if (!cur_img.data('native_width')) {
// This will create a new image object with the same image as that in .small
// We cannot directly get the dimensions from .small because of the
// width specified to 200px in the html. To get the actual dimensions we have
// created this image object.
var image_object = new Image();
image_object.onload = function() {
// This code is wrapped in the .load function which is important.
// width and height of the object would return 0 if accessed before
// the image gets loaded.
native_width = image_object.width;
native_height = image_object.height;
cur_img.data('native_width', native_width);
cur_img.data('native_height', native_height);
//console.log(native_width, native_height);
mouseMove.apply(this, arguments);
ui.glass.on('mousemove', mouseMove);
};
image_object.src = src;
return;
} else {
native_width = cur_img.data('native_width');
native_height = cur_img.data('native_height');
}
//}
//console.log(native_width, native_height);
mouseMove.apply(this, arguments);
ui.glass.on('mousemove', mouseMove);
});
ui.glass.on('mouseout', function() {
ui.glass.off('mousemove', mouseMove);
});
});
html.lb-disable-scrolling {
overflow: hidden;
/* Position fixed required for iOS. Just putting overflow: hidden; on the body is not enough. */
position: fixed;
height: 100vh;
width: 100vw;
}
.lightboxOverlay {
position: absolute;
top: 0;
left: 0;
z-index: 9999;
background-color: black;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
opacity: 0.8;
display: none;
}
.lightbox {
position: absolute;
left: 0;
width: 100%;
z-index: 10000;
text-align: center;
line-height: 0;
font-weight: normal;
}
.lightbox .lb-image {
display: block;
height: auto;
max-width: inherit;
max-height: none;
border-radius: 3px;
/* Image border */
border: 4px solid white;
}
.lightbox a img {
border: none;
}
.lb-outerContainer {
position: relative;
*zoom: 1;
width: 250px;
height: 250px;
margin: 0 auto;
border-radius: 4px;
/* Background color behind image.
This is visible during transitions. */
background-color: white;
}
.lb-outerContainer:after {
content: "";
display: table;
clear: both;
}
.lb-loader {
position: absolute;
top: 43%;
left: 0;
height: 25%;
width: 100%;
text-align: center;
line-height: 0;
}
.lb-cancel {
display: block;
width: 32px;
height: 32px;
margin: 0 auto;
background: url(../assets/imgs/lightbox/loading.gif) no-repeat;
}
.lb-nav {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 10;
}
.lb-container>.nav {
left: 0;
}
.lb-nav a {
outline: none;
background-image: url('data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==');
}
.lb-prev,
.lb-next {
height: 100%;
cursor: pointer;
display: block;
}
.lb-nav a.lb-prev {
width: 34%;
left: 0;
float: left;
background: url(../assets/imgs/lightbox/prev.png) left 48% no-repeat;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
opacity: 0;
-webkit-transition: opacity 0.6s;
-moz-transition: opacity 0.6s;
-o-transition: opacity 0.6s;
transition: opacity 0.6s;
}
.lb-nav a.lb-prev:hover {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
opacity: 1;
}
.lb-nav a.lb-next {
width: 64%;
right: 0;
float: right;
background: url(../assets/imgs/lightbox/next.png) right 48% no-repeat;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
opacity: 0;
-webkit-transition: opacity 0.6s;
-moz-transition: opacity 0.6s;
-o-transition: opacity 0.6s;
transition: opacity 0.6s;
}
.lb-nav a.lb-next:hover {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
opacity: 1;
}
.lb-dataContainer {
margin: 0 auto;
padding-top: 5px;
*zoom: 1;
width: 100%;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
.lb-dataContainer:after {
content: "";
display: table;
clear: both;
}
.lb-data {
padding: 0 4px;
color: #ccc;
}
.lb-data .lb-details {
width: 85%;
float: left;
text-align: left;
line-height: 1.1em;
}
.lb-data .lb-caption {
font-size: 13px;
font-weight: bold;
line-height: 1em;
}
.lb-data .lb-caption a {
color: #4ae;
}
.lb-data .lb-number {
display: block;
clear: left;
padding-bottom: 1em;
font-size: 12px;
color: #999999;
}
.lb-data .lb-close {
display: block;
float: right;
width: 30px;
height: 30px;
background: url(../assets/imgs/lightbox/close.png) top right no-repeat;
text-align: right;
outline: none;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70);
opacity: 0.7;
-webkit-transition: opacity 0.2s;
-moz-transition: opacity 0.2s;
-o-transition: opacity 0.2s;
transition: opacity 0.2s;
}
.lb-data .lb-close:hover {
cursor: pointer;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
opacity: 1;
}
/* IF you remove this, lightbox will work but not the magnyfing glass */
.glass {
width: 150px;
height: 150px;
position: absolute;
border-radius: 50%;
cursor: crosshair;
/* Multiple box shadows to achieve the glass effect */
box-shadow: 0 0 0 7px rgba(255, 255, 255, 0.85), 0 0 7px 7px rgba(0, 0, 0, 0.25), inset 0 0 40px 2px rgba(0, 0, 0, 0.25);
/* hide the glass by default */
display: none;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!--START BOOK PREVIEW-->
<div class="book-preview px-5 py-5 container img-magnifier-container">
<div class="row">
<div class="col-lg-4 book">
<a href="https://subsolardesigns.com/odrin/demo1/wp-content/uploads/sites/8/2017/08/cover_taurus-468x700.png" data-lightbox="image-1" data-title="My caption">
<img src="https://subsolardesigns.com/odrin/demo1/wp-content/uploads/sites/8/2017/08/cover_taurus-468x700.png" alt="Book" class="img-fluid magniflier" id="book-glass" alt="Responsive image">
</a>
</div>
</div>
</div>
The issue is that you are not clicking the image, but the .glass dom node. You need to handle the click event on the .glass element and then call click on the image, in order for the lightbox plugin to do what it expects to do. Add the code below to you javascript to redirect the click event to your image tag.
ui.glass.on('click', function() {
cur_img.click();
});