Starter Question, overlay Div with Text over an Animation - javascript

I would like to overlay a div over another div.
But there are some problems because i am newbie.
// particle.min.js hosted on GitHub
// Scroll down for initialisation code
!function(a){var b="object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global;"function"==typeof define&&define.amd?define(["exports"],function(c){b.ParticleNetwork=a(b,c)}):"object"==typeof module&&module.exports?module.exports=a(b,{}):b.ParticleNetwork=a(b,{})}(function(a,b){var c=function(a){this.canvas=a.canvas,this.g=a.g,this.particleColor=a.options.particleColor,this.x=Math.random()*this.canvas.width,this.y=Math.random()*this.canvas.height,this.velocity={x:(Math.random()-.5)*a.options.velocity,y:(Math.random()-.5)*a.options.velocity}};return c.prototype.update=function(){(this.x>this.canvas.width+20||this.x<-20)&&(this.velocity.x=-this.velocity.x),(this.y>this.canvas.height+20||this.y<-20)&&(this.velocity.y=-this.velocity.y),this.x+=this.velocity.x,this.y+=this.velocity.y},c.prototype.h=function(){this.g.beginPath(),this.g.fillStyle=this.particleColor,this.g.globalAlpha=.7,this.g.arc(this.x,this.y,1.5,0,2*Math.PI),this.g.fill()},b=function(a,b){this.i=a,this.i.size={width:this.i.offsetWidth,height:this.i.offsetHeight},b=void 0!==b?b:{},this.options={particleColor:void 0!==b.particleColor?b.particleColor:"#fff",background:void 0!==b.background?b.background:"#1a252f",interactive:void 0!==b.interactive?b.interactive:!0,velocity:this.setVelocity(b.speed),density:this.j(b.density)},this.init()},b.prototype.init=function(){if(this.k=document.createElement("div"),this.i.appendChild(this.k),this.l(this.k,{position:"absolute",top:0,left:0,bottom:0,right:0,"z-index":1}),/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(this.options.background))this.l(this.k,{background:this.options.background});else{if(!/\.(gif|jpg|jpeg|tiff|png)$/i.test(this.options.background))return console.error("Please specify a valid background image or hexadecimal color"),!1;this.l(this.k,{background:'url("'+this.options.background+'") no-repeat center',"background-size":"cover"})}if(!/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(this.options.particleColor))return console.error("Please specify a valid particleColor hexadecimal color"),!1;this.canvas=document.createElement("canvas"),this.i.appendChild(this.canvas),this.g=this.canvas.getContext("2d"),this.canvas.width=this.i.size.width,this.canvas.height=this.i.size.height,this.l(this.i,{position:"relative"}),this.l(this.canvas,{"z-index":"20",position:"relative"}),window.addEventListener("resize",function(){return this.i.offsetWidth===this.i.size.width&&this.i.offsetHeight===this.i.size.height?!1:(this.canvas.width=this.i.size.width=this.i.offsetWidth,this.canvas.height=this.i.size.height=this.i.offsetHeight,clearTimeout(this.m),void(this.m=setTimeout(function(){this.o=[];for(var a=0;a<this.canvas.width*this.canvas.height/this.options.density;a++)this.o.push(new c(this));this.options.interactive&&this.o.push(this.p),requestAnimationFrame(this.update.bind(this))}.bind(this),500)))}.bind(this)),this.o=[];for(var a=0;a<this.canvas.width*this.canvas.height/this.options.density;a++)this.o.push(new c(this));this.options.interactive&&(this.p=new c(this),this.p.velocity={x:0,y:0},this.o.push(this.p),this.canvas.addEventListener("mousemove",function(a){this.p.x=a.clientX-this.canvas.offsetLeft,this.p.y=a.clientY-this.canvas.offsetTop}.bind(this)),this.canvas.addEventListener("mouseup",function(a){this.p.velocity={x:(Math.random()-.5)*this.options.velocity,y:(Math.random()-.5)*this.options.velocity},this.p=new c(this),this.p.velocity={x:0,y:0},this.o.push(this.p)}.bind(this))),requestAnimationFrame(this.update.bind(this))},b.prototype.update=function(){this.g.clearRect(0,0,this.canvas.width,this.canvas.height),this.g.globalAlpha=1;for(var a=0;a<this.o.length;a++){this.o[a].update(),this.o[a].h();for(var b=this.o.length-1;b>a;b--){var c=Math.sqrt(Math.pow(this.o[a].x-this.o[b].x,2)+Math.pow(this.o[a].y-this.o[b].y,2));c>120||(this.g.beginPath(),this.g.strokeStyle=this.options.particleColor,this.g.globalAlpha=(120-c)/120,this.g.lineWidth=.7,this.g.moveTo(this.o[a].x,this.o[a].y),this.g.lineTo(this.o[b].x,this.o[b].y),this.g.stroke())}}0!==this.options.velocity&&requestAnimationFrame(this.update.bind(this))},b.prototype.setVelocity=function(a){return"fast"===a?1:"slow"===a?.33:"none"===a?0:.66},b.prototype.j=function(a){return"high"===a?5e3:"low"===a?2e4:isNaN(parseInt(a,10))?1e4:a},b.prototype.l=function(a,b){for(var c in b)a.style[c]=b[c]},b});
// Initialisation
var canvasDiv = document.getElementById('particle-canvas');
var options = {
particleColor: '#888',
background: 'https://raw.githubusercontent.com/JulianLaval/canvas-particle-network/master/img/demo-bg.jpg',
interactive: true,
speed: 'medium',
density: 'high'
};
var particleCanvas = new ParticleNetwork(canvasDiv, options);
html,
body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
#particle-canvas {
width: 100%;
height: 100%;
}
h1 {
text-align: center;
position: inherit;
float: none;
}
#container {
width: 100px;
height: 100px;
}
#back,
.overstyle {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 50%;
}
.overstyle h1{
font-family: 'Open Sans Condensed', sans-serif;
font-size: 230%;
font-family: 'Indie Flower', cursive;
}
.overstyle {
text-align: center;
color: white;
z-index: 100;
}
<body>
<div id="container" style="width: 100%; height: 100%;">
<div id="particle-canvas" id="back"></div>
<script src='https://rawgit.com/JulianLaval/canvas-particle-network/master/particle-network.min.js'></script>
<script src="js/index.js"></script>
<!--- Content Div --->
<div style="width: 100%; color: rgba(255, 255, 255, 0.55);" class="overstyle">
<h1>TestFont</h1>
</div>
<!--- End of Content Div --->
</div>
</body>
The code is a div with id="particle-canavas". For more information about that visit this page here.
This is my Css stylesheet.
So now how could I improve the overlay div with class "overstyle"?
Thanks for all help.

This centers as you need.
When I need to center something, I always try:
width:100%;
margin-left:auto;
margin-right:auto;
// particle.min.js hosted on GitHub
// Scroll down for initialisation code
! function(a) {
var b = "object" == typeof self && self.self === self && self || "object" == typeof global && global.global === global && global;
"function" == typeof define && define.amd ? define(["exports"], function(c) {
b.ParticleNetwork = a(b, c)
}) : "object" == typeof module && module.exports ? module.exports = a(b, {}) : b.ParticleNetwork = a(b, {})
}(function(a, b) {
var c = function(a) {
this.canvas = a.canvas, this.g = a.g, this.particleColor = a.options.particleColor, this.x = Math.random() * this.canvas.width, this.y = Math.random() * this.canvas.height, this.velocity = {
x: (Math.random() - .5) * a.options.velocity,
y: (Math.random() - .5) * a.options.velocity
}
};
return c.prototype.update = function() {
(this.x > this.canvas.width + 20 || this.x < -20) && (this.velocity.x = -this.velocity.x), (this.y > this.canvas.height + 20 || this.y < -20) && (this.velocity.y = -this.velocity.y), this.x += this.velocity.x, this.y += this.velocity.y
}, c.prototype.h = function() {
this.g.beginPath(), this.g.fillStyle = this.particleColor, this.g.globalAlpha = .7, this.g.arc(this.x, this.y, 1.5, 0, 2 * Math.PI), this.g.fill()
}, b = function(a, b) {
this.i = a, this.i.size = {
width: this.i.offsetWidth,
height: this.i.offsetHeight
}, b = void 0 !== b ? b : {}, this.options = {
particleColor: void 0 !== b.particleColor ? b.particleColor : "#fff",
background: void 0 !== b.background ? b.background : "#1a252f",
interactive: void 0 !== b.interactive ? b.interactive : !0,
velocity: this.setVelocity(b.speed),
density: this.j(b.density)
}, this.init()
}, b.prototype.init = function() {
if (this.k = document.createElement("div"), this.i.appendChild(this.k), this.l(this.k, {
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0,
"z-index": 1
}), /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(this.options.background)) this.l(this.k, {
background: this.options.background
});
else {
if (!/\.(gif|jpg|jpeg|tiff|png)$/i.test(this.options.background)) return console.error("Please specify a valid background image or hexadecimal color"), !1;
this.l(this.k, {
background: 'url("' + this.options.background + '") no-repeat center',
"background-size": "cover"
})
}
if (!/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(this.options.particleColor)) return console.error("Please specify a valid particleColor hexadecimal color"), !1;
this.canvas = document.createElement("canvas"), this.i.appendChild(this.canvas), this.g = this.canvas.getContext("2d"), this.canvas.width = this.i.size.width, this.canvas.height = this.i.size.height, this.l(this.i, {
position: "relative"
}), this.l(this.canvas, {
"z-index": "20",
position: "relative"
}), window.addEventListener("resize", function() {
return this.i.offsetWidth === this.i.size.width && this.i.offsetHeight === this.i.size.height ? !1 : (this.canvas.width = this.i.size.width = this.i.offsetWidth, this.canvas.height = this.i.size.height = this.i.offsetHeight, clearTimeout(this.m), void(this.m = setTimeout(function() {
this.o = [];
for (var a = 0; a < this.canvas.width * this.canvas.height / this.options.density; a++) this.o.push(new c(this));
this.options.interactive && this.o.push(this.p), requestAnimationFrame(this.update.bind(this))
}.bind(this), 500)))
}.bind(this)), this.o = [];
for (var a = 0; a < this.canvas.width * this.canvas.height / this.options.density; a++) this.o.push(new c(this));
this.options.interactive && (this.p = new c(this), this.p.velocity = {
x: 0,
y: 0
}, this.o.push(this.p), this.canvas.addEventListener("mousemove", function(a) {
this.p.x = a.clientX - this.canvas.offsetLeft, this.p.y = a.clientY - this.canvas.offsetTop
}.bind(this)), this.canvas.addEventListener("mouseup", function(a) {
this.p.velocity = {
x: (Math.random() - .5) * this.options.velocity,
y: (Math.random() - .5) * this.options.velocity
}, this.p = new c(this), this.p.velocity = {
x: 0,
y: 0
}, this.o.push(this.p)
}.bind(this))), requestAnimationFrame(this.update.bind(this))
}, b.prototype.update = function() {
this.g.clearRect(0, 0, this.canvas.width, this.canvas.height), this.g.globalAlpha = 1;
for (var a = 0; a < this.o.length; a++) {
this.o[a].update(), this.o[a].h();
for (var b = this.o.length - 1; b > a; b--) {
var c = Math.sqrt(Math.pow(this.o[a].x - this.o[b].x, 2) + Math.pow(this.o[a].y - this.o[b].y, 2));
c > 120 || (this.g.beginPath(), this.g.strokeStyle = this.options.particleColor, this.g.globalAlpha = (120 - c) / 120, this.g.lineWidth = .7, this.g.moveTo(this.o[a].x, this.o[a].y), this.g.lineTo(this.o[b].x, this.o[b].y), this.g.stroke())
}
}
0 !== this.options.velocity && requestAnimationFrame(this.update.bind(this))
}, b.prototype.setVelocity = function(a) {
return "fast" === a ? 1 : "slow" === a ? .33 : "none" === a ? 0 : .66
}, b.prototype.j = function(a) {
return "high" === a ? 5e3 : "low" === a ? 2e4 : isNaN(parseInt(a, 10)) ? 1e4 : a
}, b.prototype.l = function(a, b) {
for (var c in b) a.style[c] = b[c]
}, b
});
// Initialisation
var canvasDiv = document.getElementById('particle-canvas');
var options = {
particleColor: '#888',
background: 'https://raw.githubusercontent.com/JulianLaval/canvas-particle-network/master/img/demo-bg.jpg',
interactive: true,
speed: 'medium',
density: 'high'
};
var particleCanvas = new ParticleNetwork(canvasDiv, options);
html,
body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
#particle-canvas {
width: 100%;
height: 100%;
}
h1 {
text-align: center;
position: inherit;
float: none;
}
#container {
width: 100px;
height: 100px;
}
#back,
.overstyle {
width: 100%;
height: 100%;
position: absolute;
top: 0;
}
.overstyle h1{
font-family: 'Open Sans Condensed', sans-serif;
font-size: 230%;
font-family: 'Indie Flower', cursive;
width: 100%;
margin-left: auto;
margin-right: auto;
}
.overstyle {
text-align: center;
color: white;
z-index: 100;
}
<body>
<div id="container" style="width: 100%; height: 100%;">
<div id="particle-canvas" id="back"></div>
<script src='https://rawgit.com/JulianLaval/canvas-particle-network/master/particle-network.min.js'></script>
<script src="js/index.js"></script>
<!--- Content Div --->
<div style="width: 100%; color: rgba(255, 255, 255, 0.55);" class="overstyle">
<h1>TestFont</h1>
</div>
<!--- End of Content Div --->
</div>
</body>

I have tried it on the example you provided -
This worked for me:
.overstyle h1 {
z-index:2;
color: #fff;
position: absolute;
top: 20px;
left: 20px;
}

Related

add js particule in header

I tried to add the particle effect in javascript, on a project but I don't really know javascript
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Леда</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<nav class="navbar">
ADVENTURE
<div class="nav-links">
<ul>
<li>About</li>
<li>Services</li>
<li>Support</li>
<li>Infos</li>
<li>Contact</li>
</ul>
</div>
<img src="menu-btn.png" alt="menu navbar" class="menu-navbar">
</nav>
<div id="particle-canvas"></div>
</header>
<script src="script.js"></script>
</body>
</html>
css
* {
margin: 0;
padding: 0;
text-decoration: none;
list-style: none;
}
header {
background-image: url('landscape.jpg');
}
header {
height: 100vh;
width: 100vw;
background-size: cover;
}
.navbar {
position: absolute;
padding: 30px;
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
box-sizing: border-box;
}
.navbar a {
color: white;
}
.navbar .nav-links ul {
display: flex;
}
.navbar .nav-links ul li {
margin: 0 25px;
}
.navbar .nav-links ul li.active a {
margin: 0 25px;
font-weight: 600;
}
.navbar .menu-navbar{
display: none;
position: absolute;
width: 35px;
}
#media screen and (max-width: 900px) {
.navbar {
padding: 0;
}
.navbar .logo {
position: absolute;
top: 37px;
left: 50px;
}
.navbar .menu-navbar {
display: block;
top: 30px;
right: 50px;
}
.nav-links {
top: 0;
left: 0;
position: absolute;
background-color: rgba(255, 255, 255, 0.233);
width: 100%;
height: 100vh;
backdrop-filter: blur(7px);
display: flex;
justify-content: center;
align-items: center;
margin-left: -100%;
transition: all 0.5s ease;
}
.nav-links.mobile-menu {
margin-left: 0;
}
.nav-links ul {
display: flex;
flex-direction: column;
align-items: center;
}
.navbar .nav-links ul li {
margin: 25px 0;
font-size: 2em;
}
}
#particle-canvas {
width: 100%;
height: 100%;
}
js
const menuHamburger = document.querySelector(".menu-navbar")
const navLinks = document.querySelector(".nav-links")
menuHamburger.addEventListener('click', () => {
navLinks.classList.toggle('mobile-menu')
});
!function (a) { var b = "object" == typeof self && self.self === self && self || "object" == typeof global && global.global === global && global; "function" == typeof define && define.amd ? define(["exports"], function (c) { b.ParticleNetwork = a(b, c) }) : "object" == typeof module && module.exports ? module.exports = a(b, {}) : b.ParticleNetwork = a(b, {}) }(function (a, b) { var c = function (a) { this.canvas = a.canvas, this.g = a.g, this.particleColor = a.options.particleColor, this.x = Math.random() * this.canvas.width, this.y = Math.random() * this.canvas.height, this.velocity = { x: (Math.random() - .5) * a.options.velocity, y: (Math.random() - .5) * a.options.velocity } }; return c.prototype.update = function () { (this.x > this.canvas.width + 20 || this.x < -20) && (this.velocity.x = -this.velocity.x), (this.y > this.canvas.height + 20 || this.y < -20) && (this.velocity.y = -this.velocity.y), this.x += this.velocity.x, this.y += this.velocity.y }, c.prototype.h = function () { this.g.beginPath(), this.g.fillStyle = this.particleColor, this.g.globalAlpha = .7, this.g.arc(this.x, this.y, 1.5, 0, 2 * Math.PI), this.g.fill() }, b = function (a, b) { this.i = a, this.i.size = { width: this.i.offsetWidth, height: this.i.offsetHeight }, b = void 0 !== b ? b : {}, this.options = { particleColor: void 0 !== b.particleColor ? b.particleColor : "#fff", background: void 0 !== b.background ? b.background : "#1a252f", interactive: void 0 !== b.interactive ? b.interactive : !0, velocity: this.setVelocity(b.speed), density: this.j(b.density) }, this.init() }, b.prototype.init = function () { if (this.k = document.createElement("div"), this.i.appendChild(this.k), this.l(this.k, { position: "absolute", top: 0, left: 0, bottom: 0, right: 0, "z-index": 1 }), /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(this.options.background)) this.l(this.k, { background: this.options.background }); else { if (!/\.(gif|jpg|jpeg|tiff|png)$/i.test(this.options.background)) return console.error("Please specify a valid background image or hexadecimal color"), !1; this.l(this.k, { background: 'url("' + this.options.background + '") no-repeat center', "background-size": "cover" }) } if (!/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(this.options.particleColor)) return console.error("Please specify a valid particleColor hexadecimal color"), !1; this.canvas = document.createElement("canvas"), this.i.appendChild(this.canvas), this.g = this.canvas.getContext("2d"), this.canvas.width = this.i.size.width, this.canvas.height = this.i.size.height, this.l(this.i, { position: "relative" }), this.l(this.canvas, { "z-index": "20", position: "relative" }), window.addEventListener("resize", function () { return this.i.offsetWidth === this.i.size.width && this.i.offsetHeight === this.i.size.height ? !1 : (this.canvas.width = this.i.size.width = this.i.offsetWidth, this.canvas.height = this.i.size.height = this.i.offsetHeight, clearTimeout(this.m), void (this.m = setTimeout(function () { this.o = []; for (var a = 0; a < this.canvas.width * this.canvas.height / this.options.density; a++)this.o.push(new c(this)); this.options.interactive && this.o.push(this.p), requestAnimationFrame(this.update.bind(this)) }.bind(this), 500))) }.bind(this)), this.o = []; for (var a = 0; a < this.canvas.width * this.canvas.height / this.options.density; a++)this.o.push(new c(this)); this.options.interactive && (this.p = new c(this), this.p.velocity = { x: 0, y: 0 }, this.o.push(this.p), this.canvas.addEventListener("mousemove", function (a) { this.p.x = a.clientX - this.canvas.offsetLeft, this.p.y = a.clientY - this.canvas.offsetTop }.bind(this)), this.canvas.addEventListener("mouseup", function (a) { this.p.velocity = { x: (Math.random() - .5) * this.options.velocity, y: (Math.random() - .5) * this.options.velocity }, this.p = new c(this), this.p.velocity = { x: 0, y: 0 }, this.o.push(this.p) }.bind(this))), requestAnimationFrame(this.update.bind(this)) }, b.prototype.update = function () { this.g.clearRect(0, 0, this.canvas.width, this.canvas.height), this.g.globalAlpha = 1; for (var a = 0; a < this.o.length; a++) { this.o[a].update(), this.o[a].h(); for (var b = this.o.length - 1; b > a; b--) { var c = Math.sqrt(Math.pow(this.o[a].x - this.o[b].x, 2) + Math.pow(this.o[a].y - this.o[b].y, 2)); c > 120 || (this.g.beginPath(), this.g.strokeStyle = this.options.particleColor, this.g.globalAlpha = (120 - c) / 120, this.g.lineWidth = .7, this.g.moveTo(this.o[a].x, this.o[a].y), this.g.lineTo(this.o[b].x, this.o[b].y), this.g.stroke()) } } 0 !== this.options.velocity && requestAnimationFrame(this.update.bind(this)) }, b.prototype.setVelocity = function (a) { return "fast" === a ? 1 : "slow" === a ? .33 : "none" === a ? 0 : .66 }, b.prototype.j = function (a) { return "high" === a ? 5e3 : "low" === a ? 2e4 : isNaN(parseInt(a, 10)) ? 1e4 : a }, b.prototype.l = function (a, b) { for (var c in b) a.style[c] = b[c] }, b });
// Initialisation
var canvasDiv = document.getElementById('particle-canvas');
var options = {
particleColor: '#888',
interactive: true,
speed: 'medium',
density: 'high'
};
var particleCanvas = new ParticleNetwork(canvasDiv, options);
I copied and pasted the entire code but it gave me a problem. It covered my image and the navbar in blue and I can't see anything except a blue page with the particles. What needs to change in the code so that we see the image and the navbar and that the click on the navbar works.
Everything seems to be working OK except your canvas with the particles on it is 'on top' of the other stuff.
If you make the canvas go back (in the z axis sense - that is the axis that is orthogonal to the screen) then the header is seen.
Make canvas have z-index: -1;
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Леда</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<nav class="navbar">
ADVENTURE
<div class="nav-links">
<ul>
<li>About</li>
<li>Services</li>
<li>Support</li>
<li>Infos</li>
<li>Contact</li>
</ul>
</div>
<img src="menu-btn.png" alt="menu navbar" class="menu-navbar">
</nav>
<div id="particle-canvas"></div>
</header>
<style>
* {
margin: 0;
padding: 0;
text-decoration: none;
list-style: none;
}
header {
background-image: url('landscape.jpg');
}
header {
height: 100vh;
width: 100vw;
background-size: cover;
}
.navbar {
position: absolute;
padding: 30px;
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
box-sizing: border-box;
}
.navbar a {
color: white;
}
.navbar .nav-links ul {
display: flex;
}
.navbar .nav-links ul li {
margin: 0 25px;
}
.navbar .nav-links ul li.active a {
margin: 0 25px;
font-weight: 600;
}
.navbar .menu-navbar {
display: none;
position: absolute;
width: 35px;
}
#media screen and (max-width: 900px) {
.navbar {
padding: 0;
}
.navbar .logo {
position: absolute;
top: 37px;
left: 50px;
}
.navbar .menu-navbar {
display: block;
top: 30px;
right: 50px;
}
.nav-links {
top: 0;
left: 0;
position: absolute;
background-color: rgba(255, 255, 255, 0.233);
width: 100%;
height: 100vh;
backdrop-filter: blur(7px);
display: flex;
justify-content: center;
align-items: center;
margin-left: -100%;
transition: all 0.5s ease;
}
.nav-links.mobile-menu {
margin-left: 0;
}
.nav-links ul {
display: flex;
flex-direction: column;
align-items: center;
}
.navbar .nav-links ul li {
margin: 25px 0;
font-size: 2em;
}
}
#particle-canvas {
width: 100%;
height: 100%;
z-index: -1;
}
</style>
<script>
const menuHamburger = document.querySelector(".menu-navbar")
const navLinks = document.querySelector(".nav-links")
menuHamburger.addEventListener('click', () => {
navLinks.classList.toggle('mobile-menu')
});
! function(a) {
var b = "object" == typeof self && self.self === self && self || "object" == typeof global && global.global === global && global;
"function" == typeof define && define.amd ? define(["exports"], function(c) {
b.ParticleNetwork = a(b, c)
}) : "object" == typeof module && module.exports ? module.exports = a(b, {}) : b.ParticleNetwork = a(b, {})
}(function(a, b) {
var c = function(a) {
this.canvas = a.canvas, this.g = a.g, this.particleColor = a.options.particleColor, this.x = Math.random() * this.canvas.width, this.y = Math.random() * this.canvas.height, this.velocity = {
x: (Math.random() - .5) * a.options.velocity,
y: (Math.random() - .5) * a.options.velocity
}
};
return c.prototype.update = function() {
(this.x > this.canvas.width + 20 || this.x < -20) && (this.velocity.x = -this.velocity.x), (this.y > this.canvas.height + 20 || this.y < -20) && (this.velocity.y = -this.velocity.y), this.x += this.velocity.x, this.y += this.velocity.y
}, c.prototype.h = function() {
this.g.beginPath(), this.g.fillStyle = this.particleColor, this.g.globalAlpha = .7, this.g.arc(this.x, this.y, 1.5, 0, 2 * Math.PI), this.g.fill()
}, b = function(a, b) {
this.i = a, this.i.size = {
width: this.i.offsetWidth,
height: this.i.offsetHeight
}, b = void 0 !== b ? b : {}, this.options = {
particleColor: void 0 !== b.particleColor ? b.particleColor : "#fff",
background: void 0 !== b.background ? b.background : "#1a252f",
interactive: void 0 !== b.interactive ? b.interactive : !0,
velocity: this.setVelocity(b.speed),
density: this.j(b.density)
}, this.init()
}, b.prototype.init = function() {
if (this.k = document.createElement("div"), this.i.appendChild(this.k), this.l(this.k, {
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0,
"z-index": 1
}), /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(this.options.background)) this.l(this.k, {
background: this.options.background
});
else {
if (!/\.(gif|jpg|jpeg|tiff|png)$/i.test(this.options.background)) return console.error("Please specify a valid background image or hexadecimal color"), !1;
this.l(this.k, {
background: 'url("' + this.options.background + '") no-repeat center',
"background-size": "cover"
})
}
if (!/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(this.options.particleColor)) return console.error("Please specify a valid particleColor hexadecimal color"), !1;
this.canvas = document.createElement("canvas"), this.i.appendChild(this.canvas), this.g = this.canvas.getContext("2d"), this.canvas.width = this.i.size.width, this.canvas.height = this.i.size.height, this.l(this.i, {
position: "relative"
}), this.l(this.canvas, {
"z-index": "20",
position: "relative"
}), window.addEventListener("resize", function() {
return this.i.offsetWidth === this.i.size.width && this.i.offsetHeight === this.i.size.height ? !1 : (this.canvas.width = this.i.size.width = this.i.offsetWidth, this.canvas.height = this.i.size.height = this.i.offsetHeight, clearTimeout(this.m), void(this.m = setTimeout(function() {
this.o = [];
for (var a = 0; a < this.canvas.width * this.canvas.height / this.options.density; a++) this.o.push(new c(this));
this.options.interactive && this.o.push(this.p), requestAnimationFrame(this.update.bind(this))
}.bind(this), 500)))
}.bind(this)), this.o = [];
for (var a = 0; a < this.canvas.width * this.canvas.height / this.options.density; a++) this.o.push(new c(this));
this.options.interactive && (this.p = new c(this), this.p.velocity = {
x: 0,
y: 0
}, this.o.push(this.p), this.canvas.addEventListener("mousemove", function(a) {
this.p.x = a.clientX - this.canvas.offsetLeft, this.p.y = a.clientY - this.canvas.offsetTop
}.bind(this)), this.canvas.addEventListener("mouseup", function(a) {
this.p.velocity = {
x: (Math.random() - .5) * this.options.velocity,
y: (Math.random() - .5) * this.options.velocity
}, this.p = new c(this), this.p.velocity = {
x: 0,
y: 0
}, this.o.push(this.p)
}.bind(this))), requestAnimationFrame(this.update.bind(this))
}, b.prototype.update = function() {
this.g.clearRect(0, 0, this.canvas.width, this.canvas.height), this.g.globalAlpha = 1;
for (var a = 0; a < this.o.length; a++) {
this.o[a].update(), this.o[a].h();
for (var b = this.o.length - 1; b > a; b--) {
var c = Math.sqrt(Math.pow(this.o[a].x - this.o[b].x, 2) + Math.pow(this.o[a].y - this.o[b].y, 2));
c > 120 || (this.g.beginPath(), this.g.strokeStyle = this.options.particleColor, this.g.globalAlpha = (120 - c) / 120, this.g.lineWidth = .7, this.g.moveTo(this.o[a].x, this.o[a].y), this.g.lineTo(this.o[b].x, this.o[b].y), this.g.stroke())
}
}
0 !== this.options.velocity && requestAnimationFrame(this.update.bind(this))
}, b.prototype.setVelocity = function(a) {
return "fast" === a ? 1 : "slow" === a ? .33 : "none" === a ? 0 : .66
}, b.prototype.j = function(a) {
return "high" === a ? 5e3 : "low" === a ? 2e4 : isNaN(parseInt(a, 10)) ? 1e4 : a
}, b.prototype.l = function(a, b) {
for (var c in b) a.style[c] = b[c]
}, b
});
// Initialisation
var canvasDiv = document.getElementById('particle-canvas');
var options = {
particleColor: '#888',
interactive: true,
speed: 'medium',
density: 'high'
};
var particleCanvas = new ParticleNetwork(canvasDiv, options);
</script>
<script src="script.js"></script>
</body>
</html>

How to make the slider width change when Resize. JavaScript?

When I change the width of the page it does not stretch in width. Sorry for my English. #How to make the slider width change when Resize.# For some reason, it remembers the first width.
Resize width and transform Please help me. I want to change the width of the screen and have the slider adjust to the width. This code is in pure Javascript. Who can help. I've been racking my head for 3 days, I'm just a beginner.
(function() {
let curTranslateX = 0;
let curPageNum = 0;
let dots = null;
let slideWidth = 0;
let duration = 300;
let pointStart, pointMove, pointEnd;
let slidePositions = [];
let isAutoLoop = false;
let hasArrow = true;
let scrollbar = {
el: '.slide-navbar',
isHide: true,
canClick: true
};
let slideContainer = document.querySelector('.container_slider');
let slideWrapper = slideContainer.querySelector('.slide_wrapper');
let slideItems = [...slideWrapper.querySelectorAll('.slide-item')];
const utils = {
hasClass: function(elem, className) {
return(new RegExp('(\\s|^)' + className + '(\\s|$)')).test(elem.className);
},
addClass: function(elem, className) {
if(!arguments.length) {
return;
}
if(typeof className === 'undefined' || this.hasClass(elem, className)) {
return;
}
let newClasses = elem.className.split(' ');
newClasses.push(className);
elem.className = newClasses.join(' ');
},
removeClass: function(elem, className) {
if(!arguments.length) {
return;
}
if(typeof className === 'undefined' || !this.hasClass(elem, className)) {
return;
}
let classes = elem.className.split(' ');
classes = classes.filter(cls => cls !== className);
elem.className = classes.join(' ');
},
isMobile: function() {
return(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i).test(navigator.userAgent);
}
}
var slide = {
init: (function() {
document.addEventListener('DOMContentLoaded', function() {
dots = [...document.querySelectorAll('.slide-navbar span')];
dots.forEach(dot => {
utils.addClass(dot, 'dot');
});
if(utils.isMobile()) {
pointStart = 'touchstart';
pointMove = 'touchmove';
pointEnd = 'touchend';
} else {
pointStart = 'pointerdown';
pointMove = 'pointermove';
pointEnd = 'pointerup';
}
slide.bindTouchEvent();
slide.setCurrentPage();
}.bind(slide), false);
})(),
setTranslate: function(duration, offsetX, ...yz) {
this.style = `transition-duration: ${duration}ms; transform: translate3d(${offsetX}px, 0px, 0px);`;
curTranslateX = offsetX;
},
setCurrentPage: function(num) {
if(curPageNum !== -1) {
utils.removeClass(dots[curPageNum], 'dot-active');
utils.removeClass(slideItems[curPageNum], 'slide-active');
}
num = (typeof num === 'undefined') ? 0 : num;
curPageNum = num;
utils.addClass(dots[curPageNum], 'dot-active');
utils.addClass(slideItems[curPageNum], 'slide-active');
},
gotoPage: function(num) {
if(num < 0 || num > dots.length - 1) {
return;
}
slide.setTranslate.call(slideWrapper, duration, slidePositions[num]);
setTimeout(() => {
slide.setCurrentPage(num);
}, duration / 2);
},
bindTouchEvent: function() {
slideWidth = slideItems[0].scrollWidth;
slideContainer.style.width = `${slideWidth}px`;
let negMaxWidth = -slideWidth * (slideItems.length - 1);
for(let i = 0, wtd = 0; i < slideItems.length; i++, wtd -= slideWidth) {
slidePositions.push(wtd);
}
let startX,
startY,
initialPos = 0,
moveDist = 0,
direction = 0,
isMove = false,
startT = 0,
isPointOut = true;
slideContainer.addEventListener(pointStart, function(e) {
e.preventDefault();
if(!isPointOut && e.touches.length !== 1) {
return;
}
let startPoint = e.touches[0];
startX = startPoint.pageX;
startY = startPoint.pageY;
initialPos = curTranslateX;
startT = +new Date();
isMove = false;
isPointOut = false;
}.bind(this), false);
slideContainer.addEventListener(pointMove, function(e) {
if(isPointOut) {
return
}
let movePoint = e.touches[0];
let deltaX = movePoint.pageX - startX;
let deltaY = movePoint.pageY - startY;
let offsetX = initialPos + deltaX;
if(offsetX > 0 || offsetX < negMaxWidth) {
offsetX -= (deltaX / 2);
}
this.setTranslate.call(slideWrapper, 0, offsetX);
isMove = true;
deltaX = offsetX - initialPos;
moveDist = deltaX;
direction = deltaX > 0 ? 0 : 1;
}.bind(this), false);
slideContainer.addEventListener(pointEnd, function(e) {
e.preventDefault();
let deltaT = +new Date() - startT;
if(!isMove) {
if(utils.hasClass(e.target, 'slide-button-prev')) {
if(curPageNum === 0) {
return;
}
slide.gotoPage.call(e.target, curPageNum - 1);
} else if(utils.hasClass(e.target, 'slide-button-next')) {
if(curPageNum === dots.length - 1) {
return;
}
slide.gotoPage.call(e.target, curPageNum + 1);
}
return;
}
if(isPointOut) {
return;
}
isPointOut = true;
if(deltaT < 300 || Math.abs(moveDist) > slideWidth / 2) {
offsetX = direction === 0 ? curTranslateX + slideWidth - moveDist : curTranslateX - slideWidth - moveDist;
offsetX = offsetX > 0 ? 0 : offsetX;
offsetX = offsetX < negMaxWidth ? negMaxWidth : offsetX;
} else {
offsetX = curTranslateX - moveDist;
}
slide.setTranslate.call(slideWrapper, duration, offsetX);
let newPageNum = Math.round(Math.abs(offsetX) / slideWidth);
setTimeout(() => {
this.setCurrentPage(newPageNum);
}, duration / 2);
}.bind(this), false);
},
};
})();
.container_box {
max-width: 1400px;
margin: 0 auto;
}
.container_slider {
position: relative;
overflow: hidden;
}
.slide_wrapper {
position: relative;
z-index: 1;
display: flex;
transition-property: transform;
}
.container_slider .slide-item {
z-index: 1;
display: flex;
flex-shrink: 0;
width: 100%;
height: 300px;
user-select: none;
background-size: cover;
background-position: 50%;
background-repeat: no-repeat;
}
.slide-navbar {
position: absolute;
right: 0;
left: 0;
bottom: 0;
text-align: center;
font-size: 0;
z-index: 2;
}
.dot {
display: inline-block;
margin: 0 4px;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.5);
}
.dot-active {
width: 20px;
border-radius: 6px;
background-color: rgba(233, 233, 233, .9);
}
.slide-button-prev,
.slide-button-next {
display: inline-block;
position: absolute;
top: 50%;
width: 40px;
height: 60px;
z-index: 2;
color: rgba(233, 233, 233, .9);
text-align: center;
font-weight: 500;
}
.slide-button-prev {
left: 0;
transform: translateY(-50%);
}
.slide-button-next {
right: 0;
transform: translateY(-50%);
}
<div class="container_box">
<div class="container_slider">
<div class="slide_wrapper">
<div class="slide-item slide-active" style="background-color:green;"></div>
<div class="slide-item" style="background-color:blue;"></div>
<div class="slide-item" style="background-color:black;"></div>
</div>
<div class="control_slider slide-button-prev"><</div>
<div class="control_slider slide-button-next">></div>
<div class="slide-navbar">
<span class="dot dot-active">1</span>
<span class="dot">2</span>
<span class="dot">3</span>
</div>
</div>
</div>
First: I replaced the < and > by > and <:
<div class="control_slider slide-button-prev"><</div>
<div class="control_slider slide-button-next">></div>
To your question:
I found out that in bindTouchEvent: function() {}
exists the line
slideContainer.style.width = ${slideWidth}px;
This line is in charge to set a fix width. If you comment out this line all single slide-items reacts on resize.

Online Scratch Card Splice from array and call function when empty

here I have a script of a scratch card game that works. For now I want to ask a splice function where the image in the array will be removed for each attempt online and a function will execute when the array is empty. So far I can't seem to get it work. Maybe need to add somewhere at the library. Below are the scripts:
var promoCode = '';
var bg1 = 'http://farm5.static.flickr.com/4005/4706825697_c0367e6dee_b.jpg';
var bg2 = 'http://farm5.static.flickr.com/4017/4717107886_dcc1270a65_b.jpg';
var bg3 = 'http://images6.fanpop.com/image/photos/41500000/adorable-puppies-cute-puppies-41538743-590-393.jpg';
var bgArray = [bg1, bg2, bg3];
selectBG = bgArray.splice(Math.floor(Math.random() * bgArray.length), 1)[0];
console.log(selectBG);
if (selectBG === bg1) {
promoCode = 'SCRATCH400';
} else if (selectBG === bg2) {
promoCode = 'SCRATCH500';
} else if (selectBG === bg3) {
promoCode = 'SCRATCH600';
} else if (bgArray.length === 0) {
alert("No more voucher");
}
$('#promo').wScratchPad({
// the size of the eraser
size: 70,
// the randomized scratch image
bg: selectBG,
// give real-time updates
realtime: true,
// The overlay image
fg: 'https://cdn.glitch.com/2c225b7b-6134-4f2b-9842-1d5d060d9cd4%2Foverlay.png',
// The cursor (coin) image
'cursor': 'url("https://cdn.glitch.com/2c225b7b-6134-4f2b-9842-1d5d060d9cd4%2Fcoin1.png") 5 5, default',
scratchMove: function(e, percent) {
console.log(percent);
console.log(promoCode);
// Show the plain-text promo code and call-to-action when the scratch area is 50% scratched
if ((percent > 50) && (promoCode != '')) {
$('.promo-container').show();
$('body').removeClass('not-selectable');
$('.promo-code').html('Your code is: ' + promoCode);
}
}
});
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
.scratchpad {
width: 450px;
height: 445px;
border: solid 10px #FFFFFF;
margin: 0 auto;
}
body {
background: #efefef;
}
.scratch-container {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
width: 100%;
}
#media only screen and (max-width: 480px) {
.scratchpad {
width: 400px;
height: 396px;
}
.scratch-container {
width: 400px !important;
}
}
/* Custom, iPhone Retina */
#media only screen and (max-width: 320px) {
.scratchpad {
width: 290px;
height: 287px;
}
.scratch-container {
width: 290px !important;
}
}
.promo-container {
background: #FFF;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
width: 450px;
padding: 20px;
margin: 0 auto;
text-align: center;
font-family: 'Open Sans', Arial, Sans-serif;
color: #333;
font-size: 16px;
margin-top: 20px;
}
.btn {
background: #56CFD2;
color: #FFF;
padding: 10px 25px;
display: inline-block;
margin-top: 15px;
text-decoration: none;
font-weight: 600;
text-transform: uppercase;
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radiuss: 3px;
}
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,300,300italic,400italic,600,600italic,700italic,700" rel="stylesheet" type="text/css">
<title>Scratch Card</title>
</head>
<body>
<div class="scratch-container">
<div id="promo" class="scratchpad"></div>
</div>
<div class="promo-container" style="display:none;">
<div class="promo-code"></div>
Return
</div>
<script type="text/javascript">
(function(a) {
'use strict';
function b(c, d) {
this.$el = a(c), this.options = d, this.init = !1, this.enabled = !0, this._generate()
}
b.prototype = {
_generate: function() {
return a.support.canvas ? void(this.canvas = document.createElement('canvas'), this.ctx = this.canvas.getContext('2d'), 'static' === this.$el.css('position') && this.$el.css('position', 'relative'), this.$img = a('<img src=""/>').attr('crossOrigin', '').css({
position: 'absolute',
width: '100%',
height: '100%'
}), this.$scratchpad = a(this.canvas).css({
position: 'absolute',
width: '100%',
height: '100%'
}), this.$scratchpad.bindMobileEvents(), this.$scratchpad.mousedown(a.proxy(function(c) {
return !this.enabled || void(this.canvasOffset = a(this.canvas).offset(), this.scratch = !0, this._scratchFunc(c, 'Down'))
}, this)).mousemove(a.proxy(function(c) {
this.scratch && this._scratchFunc(c, 'Move')
}, this)).mouseup(a.proxy(function(c) {
this.scratch && (this.scratch = !1, this._scratchFunc(c, 'Up'))
}, this)), this._setOptions(), this.$el.append(this.$img).append(this.$scratchpad), this.init = !0, this.reset()) : (this.$el.append('Canvas is not supported in this browser.'), !0)
},
reset: function() {
var c = this,
d = Math.ceil(this.$el.innerWidth()),
f = Math.ceil(this.$el.innerHeight()),
g = window.devicePixelRatio || 1;
this.pixels = d * f, this.$scratchpad.attr('width', d).attr('height', f), this.canvas.setAttribute('width', d * g), this.canvas.setAttribute('height', f * g), this.ctx.scale(g, g), this.pixels = d * g * f * g, this.$img.hide(), this.options.bg && ('#' === this.options.bg.charAt(0) ? this.$el.css('backgroundColor', this.options.bg) : (this.$el.css('backgroundColor', ''), this.$img.attr('src', this.options.bg))), this.options.fg && ('#' === this.options.fg.charAt(0) ? (this.ctx.fillStyle = this.options.fg, this.ctx.beginPath(), this.ctx.rect(0, 0, d, f), this.ctx.fill(), this.$img.show()) : a(new Image).attr('crossOrigin', '').attr('src', this.options.fg).load(function() {
c.ctx.drawImage(this, 0, 0, d, f), c.$img.show()
}))
},
clear: function() {
this.ctx.clearRect(0, 0, Math.ceil(this.$el.innerWidth()), Math.ceil(this.$el.innerHeight()))
},
enable: function(c) {
this.enabled = !(!0 !== c)
},
destroy: function() {
this.$el.children().remove(), a.removeData(this.$el, 'wScratchPad')
},
_setOptions: function() {
var c, d;
for (c in this.options) this.options[c] = this.$el.attr('data-' + c) || this.options[c], d = 'set' + c.charAt(0).toUpperCase() + c.substring(1), this[d] && this[d](this.options[c])
},
setBg: function() {
this.init && this.reset()
},
setFg: function() {
this.setBg()
},
setCursor: function(c) {
this.$el.css('cursor', c)
},
_scratchFunc: function(c, d) {
c.pageX = Math.floor(c.pageX - this.canvasOffset.left), c.pageY = Math.floor(c.pageY - this.canvasOffset.top), this['_scratch' + d](c), (this.options.realtime || 'Up' === d) && this.options['scratch' + d] && this.options['scratch' + d].apply(this, [c, this._scratchPercent()])
},
_scratchPercent: function() {
for (var c = 0, d = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height), f = 0, g = d.data.length; f < g; f += 4) 0 === d.data[f] && 0 === d.data[f + 1] && 0 === d.data[f + 2] && 0 === d.data[f + 3] && c++;
return 100 * (c / this.pixels)
},
_scratchDown: function(c) {
this.ctx.globalCompositeOperation = 'destination-out', this.ctx.lineJoin = 'round', this.ctx.lineCap = 'round', this.ctx.strokeStyle = this.options.color, this.ctx.lineWidth = this.options.size, this.ctx.beginPath(), this.ctx.arc(c.pageX, c.pageY, this.options.size / 2, 0, 2 * Math.PI, !0), this.ctx.closePath(), this.ctx.fill(), this.ctx.beginPath(), this.ctx.moveTo(c.pageX, c.pageY)
},
_scratchMove: function(c) {
this.ctx.lineTo(c.pageX, c.pageY), this.ctx.stroke()
},
_scratchUp: function() {
this.ctx.closePath()
}
}, a.support.canvas = document.createElement('canvas').getContext, a.fn.wScratchPad = function(c, d) {
if ('string' == typeof c) {
var g, h = [],
j = (void 0 === d ? 'get' : 'set') + c.charAt(0).toUpperCase() + c.substring(1),
k = function() {
g.options[c] && (g.options[c] = d), g[j] && g[j].apply(g, [d])
},
l = function() {
return g[j] ? g[j].apply(g, [d]) : g.options[c] ? g.options[c] : void 0
},
m = function() {
g = a.data(this, 'wScratchPad'), g && (g[c] ? g[c].apply(g, [d]) : void 0 === d ? h.push(l()) : k())
};
return this.each(m), h.length ? 1 === h.length ? h[0] : h : this
}
return c = a.extend({}, a.fn.wScratchPad.defaults, c), this.each(function() {
var n = a.data(this, 'wScratchPad');
return n || (n = new b(this, a.extend(!0, {}, c)), a.data(this, 'wScratchPad', n)), n
})
}, a.fn.wScratchPad.defaults = {
size: 5,
bg: '#cacaca',
fg: '#6699ff',
realtime: !0,
scratchDown: null,
scratchUp: null,
scratchMove: null,
cursor: 'crosshair'
}, a.fn.bindMobileEvents = function() {
a(this).on('touchstart touchmove touchend touchcancel', function(c) {
var d = c.changedTouches || c.originalEvent.targetTouches,
f = d[0],
g = '';
switch (c.type) {
case 'touchstart':
g = 'mousedown';
break;
case 'touchmove':
g = 'mousemove', c.preventDefault();
break;
case 'touchend':
g = 'mouseup';
break;
default:
return;
}
var h = document.createEvent('MouseEvent');
h.initMouseEvent(g, !0, !0, window, 1, f.screenX, f.screenY, f.clientX, f.clientY, !1, !1, !1, !1, 0, null), f.target.dispatchEvent(h)
})
}
})(jQuery);
</script>
</body>
</html>

Configure Javascript Range Slider

I have a very simple range slider here: https://jsfiddle.net/dv45kseb/
I'd like to be able to dynamically display 50% of the slider value in the <h3> but I'm not sure to start.
//slider javascript
/*! rangeslider.js - v2.0.2 | (c) 2015 #andreruffert | MIT license | https://github.com/andreruffert/rangeslider.js */
! function(a) {
"use strict";
"function" == typeof define && define.amd ? define(["jquery"], a) : a("object" == typeof exports ? require("jquery") : jQuery)
}(function(a) {
"use strict";
function b() {
var a = document.createElement("input");
return a.setAttribute("type", "range"), "text" !== a.type
}
function c(a, b) {
var c = Array.prototype.slice.call(arguments, 2);
return setTimeout(function() {
return a.apply(null, c)
}, b)
}
function d(a, b) {
return b = b || 100,
function() {
if (!a.debouncing) {
var c = Array.prototype.slice.apply(arguments);
a.lastReturnVal = a.apply(window, c), a.debouncing = !0
}
return clearTimeout(a.debounceTimeout), a.debounceTimeout = setTimeout(function() {
a.debouncing = !1
}, b), a.lastReturnVal
}
}
function e(a) {
return a && (0 === a.offsetWidth || 0 === a.offsetHeight || a.open === !1)
}
function f(a) {
for (var b = [], c = a.parentNode; e(c);) b.push(c), c = c.parentNode;
return b
}
function g(a, b) {
function c(a) {
"undefined" != typeof a.open && (a.open = a.open ? !1 : !0)
}
var d = f(a),
e = d.length,
g = [],
h = a[b];
if (e) {
for (var i = 0; e > i; i++) g[i] = d[i].style.cssText, d[i].style.display = "block", d[i].style.height = "0", d[i].style.overflow = "hidden", d[i].style.visibility = "hidden", c(d[i]);
h = a[b];
for (var j = 0; e > j; j++) d[j].style.cssText = g[j], c(d[j])
}
return h
}
function h(a, b) {
var c = parseFloat(a);
return Number.isNaN(c) ? b : c
}
function i(a) {
return a.charAt(0).toUpperCase() + a.substr(1)
}
function j(b, e) {
if (this.$window = a(window), this.$document = a(document), this.$element = a(b), this.options = a.extend({}, n, e), this.polyfill = this.options.polyfill, this.orientation = this.$element[0].getAttribute("data-orientation") || this.options.orientation, this.onInit = this.options.onInit, this.onSlide = this.options.onSlide, this.onSlideEnd = this.options.onSlideEnd, this.DIMENSION = o.orientation[this.orientation].dimension, this.DIRECTION = o.orientation[this.orientation].direction, this.DIRECTION_STYLE = o.orientation[this.orientation].directionStyle, this.COORDINATE = o.orientation[this.orientation].coordinate, this.polyfill && m) return !1;
this.identifier = "js-" + k + "-" + l++, this.startEvent = this.options.startEvent.join("." + this.identifier + " ") + "." + this.identifier, this.moveEvent = this.options.moveEvent.join("." + this.identifier + " ") + "." + this.identifier, this.endEvent = this.options.endEvent.join("." + this.identifier + " ") + "." + this.identifier, this.toFixed = (this.step + "").replace(".", "").length - 1, this.$fill = a('<div class="' + this.options.fillClass + '" />'), this.$handle = a('<div class="' + this.options.handleClass + '" />'), this.$range = a('<div class="' + this.options.rangeClass + " " + this.options[this.orientation + "Class"] + '" id="' + this.identifier + '" />').insertAfter(this.$element).prepend(this.$fill, this.$handle), this.$element.css({
position: "absolute",
width: "1px",
height: "1px",
overflow: "hidden",
opacity: "0"
}), this.handleDown = a.proxy(this.handleDown, this), this.handleMove = a.proxy(this.handleMove, this), this.handleEnd = a.proxy(this.handleEnd, this), this.init();
var f = this;
this.$window.on("resize." + this.identifier, d(function() {
c(function() {
f.update()
}, 300)
}, 20)), this.$document.on(this.startEvent, "#" + this.identifier + ":not(." + this.options.disabledClass + ")", this.handleDown), this.$element.on("change." + this.identifier, function(a, b) {
if (!b || b.origin !== f.identifier) {
var c = a.target.value,
d = f.getPositionFromValue(c);
f.setPosition(d)
}
})
}
Number.isNaN = Number.isNaN || function(a) {
return "number" == typeof a && a !== a
};
var k = "rangeslider",
l = 0,
m = b(),
n = {
polyfill: !0,
orientation: "horizontal",
rangeClass: "rangeslider",
disabledClass: "rangeslider--disabled",
horizontalClass: "rangeslider--horizontal",
verticalClass: "rangeslider--vertical",
fillClass: "rangeslider__fill",
handleClass: "rangeslider__handle",
startEvent: ["mousedown", "touchstart", "pointerdown"],
moveEvent: ["mousemove", "touchmove", "pointermove"],
endEvent: ["mouseup", "touchend", "pointerup"]
},
o = {
orientation: {
horizontal: {
dimension: "width",
direction: "left",
directionStyle: "left",
coordinate: "x"
},
vertical: {
dimension: "height",
direction: "top",
directionStyle: "bottom",
coordinate: "y"
}
}
};
j.prototype.init = function() {
this.update(!0, !1), this.$element[0].value = this.value, this.onInit && "function" == typeof this.onInit && this.onInit()
}, j.prototype.update = function(a, b) {
a = a || !1, a && (this.min = h(this.$element[0].getAttribute("min"), 0), this.max = h(this.$element[0].getAttribute("max"), 100), this.value = h(this.$element[0].value, this.min + (this.max - this.min) / 2), this.step = h(this.$element[0].getAttribute("step"), 1)), this.handleDimension = g(this.$handle[0], "offset" + i(this.DIMENSION)), this.rangeDimension = g(this.$range[0], "offset" + i(this.DIMENSION)), this.maxHandlePos = this.rangeDimension - this.handleDimension, this.grabPos = this.handleDimension / 2, this.position = this.getPositionFromValue(this.value), this.$element[0].disabled ? this.$range.addClass(this.options.disabledClass) : this.$range.removeClass(this.options.disabledClass), this.setPosition(this.position, b)
}, j.prototype.handleDown = function(a) {
if (a.preventDefault(), this.$document.on(this.moveEvent, this.handleMove), this.$document.on(this.endEvent, this.handleEnd), !((" " + a.target.className + " ").replace(/[\n\t]/g, " ").indexOf(this.options.handleClass) > -1)) {
var b = this.getRelativePosition(a),
c = this.$range[0].getBoundingClientRect()[this.DIRECTION],
d = this.getPositionFromNode(this.$handle[0]) - c,
e = "vertical" === this.orientation ? this.maxHandlePos - (b - this.grabPos) : b - this.grabPos;
this.setPosition(e), b >= d && b < d + this.handleDimension && (this.grabPos = b - d)
}
}, j.prototype.handleMove = function(a) {
a.preventDefault();
var b = this.getRelativePosition(a),
c = "vertical" === this.orientation ? this.maxHandlePos - (b - this.grabPos) : b - this.grabPos;
this.setPosition(c)
}, j.prototype.handleEnd = function(a) {
a.preventDefault(), this.$document.off(this.moveEvent, this.handleMove), this.$document.off(this.endEvent, this.handleEnd), this.$element.trigger("change", {
origin: this.identifier
}), this.onSlideEnd && "function" == typeof this.onSlideEnd && this.onSlideEnd(this.position, this.value)
}, j.prototype.cap = function(a, b, c) {
return b > a ? b : a > c ? c : a
}, j.prototype.setPosition = function(a, b) {
var c, d;
void 0 === b && (b = !0), c = this.getValueFromPosition(this.cap(a, 0, this.maxHandlePos)), d = this.getPositionFromValue(c), this.$fill[0].style[this.DIMENSION] = d + this.grabPos + "px", this.$handle[0].style[this.DIRECTION_STYLE] = d + "px", this.setValue(c), this.position = d, this.value = c, b && this.onSlide && "function" == typeof this.onSlide && this.onSlide(d, c)
}, j.prototype.getPositionFromNode = function(a) {
for (var b = 0; null !== a;) b += a.offsetLeft, a = a.offsetParent;
return b
}, j.prototype.getRelativePosition = function(a) {
var b = i(this.COORDINATE),
c = this.$range[0].getBoundingClientRect()[this.DIRECTION],
d = 0;
return "undefined" != typeof a["page" + b] ? d = a["client" + b] : "undefined" != typeof a.originalEvent["client" + b] ? d = a.originalEvent["client" + b] : a.originalEvent.touches && a.originalEvent.touches[0] && "undefined" != typeof a.originalEvent.touches[0]["client" + b] ? d = a.originalEvent.touches[0]["client" + b] : a.currentPoint && "undefined" != typeof a.currentPoint[this.COORDINATE] && (d = a.currentPoint[this.COORDINATE]), d - c
}, j.prototype.getPositionFromValue = function(a) {
var b, c;
return b = (a - this.min) / (this.max - this.min), c = Number.isNaN(b) ? 0 : b * this.maxHandlePos
}, j.prototype.getValueFromPosition = function(a) {
var b, c;
return b = a / (this.maxHandlePos || 1), c = this.step * Math.round(b * (this.max - this.min) / this.step) + this.min, Number(c.toFixed(this.toFixed))
}, j.prototype.setValue = function(a) {
a !== this.value && this.$element.val(a).trigger("input", {
origin: this.identifier
})
}, j.prototype.destroy = function() {
this.$document.off("." + this.identifier), this.$window.off("." + this.identifier), this.$element.off("." + this.identifier).removeAttr("style").removeData("plugin_" + k), this.$range && this.$range.length && this.$range[0].parentNode.removeChild(this.$range[0])
}, a.fn[k] = function(b) {
var c = Array.prototype.slice.call(arguments, 1);
return this.each(function() {
var d = a(this),
e = d.data("plugin_" + k);
e || d.data("plugin_" + k, e = new j(this, b)), "string" == typeof b && e[b].apply(e, c)
})
}
});
//custom slider javascript
$(function() {
var output = document.querySelectorAll('output')[0];
$(document).on('input', 'input[type="range"]', function(e) {
output.innerHTML = e.currentTarget.value;
});
$('input[type=range]').rangeslider({
polyfill: false
});
});
//when slider changes, hide start message
$("input").on("change", function() {
$("small").fadeOut("slow");
})
body {
color: #404040;
padding: 50px;
max-width: 500px;
margin: 0 auto;
text-align: center;
font-family: sans-serif;
}
h1 {
font-weight: 300;
}
#helper {
float: left;
margin-top: 20px;
color: #46b7d5;
font-style: italic;
}
output {
display: block;
font-size: 3em;
}
/* original css */
.rangeslider,
.rangeslider__fill {
display: block;
-moz-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3);
box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3);
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
}
.rangeslider {
background: #e6e6e6;
position: relative;
}
.rangeslider--horizontal {
height: 20px;
width: 100%;
}
.rangeslider--vertical {
width: 20px;
min-height: 150px;
max-height: 100%;
}
.rangeslider--disabled {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40);
opacity: 0.4;
}
.rangeslider__fill {
background: -webkit-linear-gradient(left, #abe0ed, #46b7d5);
/* For Safari 5.1 to 6.0 */
background: -o-linear-gradient(right, #abe0ed, #46b7d5);
/* For Opera 11.1 to 12.0 */
background: -moz-linear-gradient(right, #abe0ed, #46b7d5);
/* For Firefox 3.6 to 15 */
background: linear-gradient(to right, #abe0ed, #46b7d5);
/* Standard syntax (must be last) */
position: absolute;
}
.rangeslider--horizontal .rangeslider__fill {
top: 0;
height: 100%;
}
.rangeslider--vertical .rangeslider__fill {
bottom: 0;
width: 100%;
}
.rangeslider__handle {
background: white;
border: 1px solid #ccc;
cursor: pointer;
display: inline-block;
width: 40px;
height: 40px;
position: absolute;
background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4xIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g");
background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(100%, rgba(0, 0, 0, 0.1)));
background-image: -moz-linear-gradient(rgba(255, 255, 255, 0), rgba(0, 0, 0, 0.1));
background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0), rgba(0, 0, 0, 0.1));
background-image: linear-gradient(rgba(255, 255, 255, 0), rgba(0, 0, 0, 0.1));
-moz-box-shadow: 0 0 8px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 0.3);
box-shadow: 0 0 8px rgba(0, 0, 0, 0.3);
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
}
.rangeslider__handle:after {
content: "";
display: block;
width: 18px;
height: 18px;
margin: auto;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjEzIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjZmZmZmZmIiBzdG9wLW9wYWNpdHk9IjAuMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==");
background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(0, 0, 0, 0.13)), color-stop(100%, rgba(255, 255, 255, 0)));
background-image: -moz-linear-gradient(rgba(0, 0, 0, 0.13), rgba(255, 255, 255, 0));
background-image: -webkit-linear-gradient(rgba(0, 0, 0, 0.13), rgba(255, 255, 255, 0));
background-image: linear-gradient(rgba(0, 0, 0, 0.13), rgba(255, 255, 255, 0));
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
}
.rangeslider__handle:active {
background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjEiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4xMiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==");
background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(0, 0, 0, 0.1)), color-stop(100%, rgba(0, 0, 0, 0.12)));
background-image: -moz-linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.12));
background-image: -webkit-linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.12));
background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.12));
}
.rangeslider--horizontal .rangeslider__handle {
top: -10px;
touch-action: pan-y;
-ms-touch-action: pan-y;
}
.rangeslider--vertical .rangeslider__handle {
left: -10px;
touch-action: pan-x;
-ms-touch-action: pan-x;
}
input[type="range"]:focus + .rangeslider .rangeslider__handle {
-moz-box-shadow: 0 0 8px rgba(255, 0, 255, 0.9);
-webkit-box-shadow: 0 0 8px rgba(255, 0, 255, 0.9);
box-shadow: 0 0 8px rgba(255, 0, 255, 0.9);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<h1>
This form lets you drag the slider.
</h1>
<!--https://andreruffert.github.io/rangeslider.js/-->
<input type="range" value="3825" step="500" min="1000" max="40000">
<small id="helper" class="slideRight">Slide to get started →</small>
<br>
<br>
<h2 style="display: inline-block; margin-bottom: 50px;"></h2><output style="display: inline-block">3,825</output>
<h3>
<< Display 50% of total value here >>
</h3>
This part of the code is where the value in the header is modified:
$(document).on('input', 'input[type="range"]', function(e) {
output.innerHTML = e.currentTarget.value;
});
You just need to divide your the value like this:
output.innerHTML = e.currentTarget.value / 2;
To complete the answer - any time you need a value filled dynamically in a page by a javascript you can enclose it with a SPAN tag and an ID attribute (because IDs are unique). So:
<h3>
<span id="halfvalue"></span>
</h3>
and then you add additional code to your event handler code:
var output = document.querySelectorAll('output')[0];
var halfvalue = document.querySelector('#halfvalue');
$(document).on('input', 'input[type="range"]', function(e) {
output.innerHTML = e.currentTarget.value;
halfvalue.innerHTML = e.currentTarget.value / 2
});

Drag image within container, not moving with mouse, slightly quicker

I have got a Vue component where i can zoom in on an image and then move it around the container. When zoomed there is also a small viewport to show what part of the image is visible. However when moving the image around it is moving faster than the mouse, i'm guessing this is due to using the scale transform.
I also feel like when i'm clicking and dragging on the viewport I shouldn't be reversing the values twice however this seems to be the only way to get it moving the square with the mouse.
Vue.component('test', {
template: '#template',
data: function() {
return {
loading: true,
loop: true,
speed: 8,
speedController: 0,
zoomEnabled: true,
zoomLevels: [1, 1.5, 2, 2.5, 3],
zoomLevel: 1,
frame: 1,
images: [],
imagesPreloaded: 0,
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,
};
},
mounted() {
window.addEventListener('mouseup', this.handleEnd);
window.addEventListener('touchend', this.handleEnd);
},
beforeDestroy() {
window.removeEventListener('mouseup', this.handleEnd);
window.removeEventListener('touchend', this.handleEnd);
},
methods: {
handleSlider(event) {
this.frame = Number(event.target.value);
},
zoom(direction) {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
if (this.zoomLevels[this.zoomLevels.indexOf(closest) + direction] === undefined) {
return;
}
let current = this.zoomLevels.indexOf(closest);
let index = current += direction;
if (direction === 0) {
index = 0;
}
this.zoomLevel = this.zoomLevels[index];
window.requestAnimationFrame(() => {
this.translate(null, this.$refs.image, true);
});
},
zoomWheel($event) {
$event.preventDefault();
this.zoomLevel += $event.deltaY * -0.01;
if (this.zoomLevel < 1) {
this.zoomLevel = 1;
}
let maxZoom = this.zoomLevels[this.zoomLevels.length - 1];
this.zoomLevel = Math.min(Math.max(.125, this.zoomLevel), maxZoom);
window.requestAnimationFrame(() => {
this.translate(null, this.$refs.image, true);
});
},
handleStart($event) {
$event.preventDefault();
if ($event.button && $event.button !== 0) {
return;
}
this.isMoving = true;
this.isDragging = true;
this.startX = $event.pageX || $event.touches[0].pageX;
this.startY = $event.pageY || $event.touches[0].pageY;
},
handleMove($event, viewport) {
if ($event.button && $event.button !== 0) {
return;
}
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, $event.target, null, viewport);
}
if (this.zoomLevel === 1) {
this.changeFrame(positions);
}
}
},
handleEnd($event) {
if ($event.button && $event.button !== 0) {
return;
}
this.isMoving = false;
},
translate(positions, element, zooming, viewport) {
if (positions === null) {
positions = {
x: this.startX,
y: this.startY
};
}
let move = {
x: Math.floor(positions.x - this.startX),
y: Math.floor(positions.y - this.startY)
};
// Reverse Mouse Movement
if (viewport) {
move.x = -move.x;
move.y = -move.y;
}
let image = element.getBoundingClientRect();
let container = element.parentNode.getBoundingClientRect();
let translate = {
left: Math.floor((container.left - image.left) - (move.x * this.zoomLevel)),
right: Math.floor((container.right - image.right) - (move.x * this.zoomLevel)),
top: Math.floor((container.top - image.top) - (move.y * this.zoomLevel)),
bottom: Math.floor((container.bottom - image.bottom) - (move.y * this.zoomLevel))
};
// Reverse Translate Movement
if (viewport) {
translate.left = -translate.left;
translate.right = -translate.right;
translate.top = -translate.top;
translate.bottom = -translate.bottom;
}
if (zooming) {
if (translate.left <= 0) {
this.translateX += Math.floor(translate.left);
}
if (translate.right >= 0) {
this.translateX += Math.floor(translate.right);
}
if (translate.top <= 0) {
this.translateY += Math.floor(translate.top);
}
if (translate.bottom >= 0) {
this.translateY += Math.floor(translate.bottom);
}
}
if (translate.left >= 0 && translate.right <= 0) {
this.translateX += Math.floor(move.x);
}
if (translate.top >= 0 && translate.bottom <= 0) {
this.translateY += Math.floor(move.y);
}
this.startX = positions.x;
this.startY = positions.y;
}
},
computed: {
nextZoomLevel: function() {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
if (this.zoomLevels.indexOf(closest) === this.zoomLevels.length - 1) {
return this.zoomLevels[0];
}
return this.zoomLevels[this.zoomLevels.indexOf(closest) + 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() {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
return this.zoomLevels[this.zoomLevels.indexOf(closest) + 1] === undefined
},
canZoomOut: function() {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
return this.zoomLevels[this.zoomLevels.indexOf(closest) + -1] === undefined
}
}
});
window.vue = new Vue({el: '#app'});
.media-360-viewer {
position: relative;
overflow: hidden;
background: #000;
}
.media-360-viewer>img {
width: 100%;
}
.media-360-viewer>img.canTranslate {
cursor: grab;
}
.media-360-viewer>img.isTranslating {
cursor: grabbing;
}
.media-360-viewer>img.canRotate {
cursor: w-resize;
}
.media-360-viewer__loader {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
}
.media-360-viewer__loader * {
user-select: none;
}
.media-360-viewer__loader>svg {
width: 100%;
height: 100%;
transform: rotate(270deg);
}
.media-360-viewer__loader--text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.media-360-viewer__loader--text p {
font-size: 100%;
font-weight: bold;
color: #fff;
}
.media-360-viewer__loader--text p.large {
font-size: 150%;
}
.media-360-viewer__loader--background {
stroke-dasharray: 0;
stroke-dashoffset: 0;
stroke: rgba(0, 0, 0, 0.7);
stroke-width: 25px;
}
.media-360-viewer__loader--cover {
stroke-dasharray: 200%;
stroke: #848484;
stroke-width: 15px;
stroke-linecap: round;
}
.media-360-viewer__loader--background,
.media-360-viewer__loader--cover {
fill: transparent;
}
.media-360-viewer__viewport {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
border: 1px solid black;
overflow: hidden;
}
.media-360-viewer__viewport--image {
width: 100%;
pointer-events: none;
}
.media-360-viewer__viewport--zoom {
position: absolute;
bottom: 5px;
right: 5px;
color: #fff;
z-index: 3;
font-size: 12px;
pointer-events: none;
}
.media-360-viewer__viewport--square {
border: 1px solid black;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
box-shadow: rgba(0, 0, 0, 0.5) 0 0 0 10000px;
cursor: grab;
transition: background ease-in-out 0.1s;
}
.media-360-viewer__viewport--square:hover {
background: rgba(255, 255, 255, 0.2);
}
.media-360-viewer__header {
position: absolute;
top: 10px;
left: 0;
width: 100%;
}
.media-360-viewer__tools {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 10px;
}
.media-360-viewer__tools>a {
margin: 0 5px;
color: #000;
background: #fff;
border-radius: 50%;
width: 40px;
text-align: center;
line-height: 40px;
}
.media-360-viewer__tools>a[disabled] {
opacity: .5;
cursor: not-allowed;
}
.media-360-viewer__tools>a[disabled]:hover {
color: #000;
background: #fff;
}
.media-360-viewer__tools>a:hover {
background: #000;
color: #fff;
}
.media-360-viewer__tools--autoplay:before {
font-family: 'ClickIcons';
content: '\ea81';
}
.media-360-viewer__tools--autoplay.active:before {
content: '\eb48';
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<test></test>
</div>
<script type="text/x-template" id="template">
<div class="media-360-viewer" ref="container">
<transition name="fade">
<div class="media-360-viewer__viewport" v-if="zoomLevel > 1 && viewportEnabled" :style="{ width: (viewportScale * 100) + '%' }">
<img tabindex="1" draggable="false" alt="Viewport" class="media-360-viewer__viewport--image" src="https://www.bennetts.co.uk/-/media/bikesocial/2019-september-images/2020-yamaha-yzf-r1-and-r1m-review/2020-yamaha-r1-and-r1m_005.ashx?h=493&w=740&la=en&hash=F97CD240F0DDFA9540E912DCF7F07019017035C6">
<span class="media-360-viewer__viewport--zoom">
x{{ Math.round(zoomLevel * 10) / 10 }}
</span>
<span :style="{ transform: viewportTransform }" #mouseup="handleEnd" #mousedown="handleStart" #mousemove="handleMove($event, true)" #touchstart="handleStart" #touchend="handleEnd" #touchmove="handleMove($event, true)" class="media-360-viewer__viewport--square"></span>
</div>
</transition>
<img tabindex="1" ref="image" draggable="false" src="https://www.bennetts.co.uk/-/media/bikesocial/2019-september-images/2020-yamaha-yzf-r1-and-r1m-review/2020-yamaha-r1-and-r1m_005.ashx?h=493&w=740&la=en&hash=F97CD240F0DDFA9540E912DCF7F07019017035C6" :style="{ transform: transform }" :class="{
canTranslate: zoomLevel > 1 && zoomEnabled,
canRotate: zoomLevel === 1,
isTranslating: zoomLevel > 1 && zoomEnabled && isMoving
}" #mouseup="handleEnd" #mousedown="handleStart" #mousemove="handleMove" #touchstart="handleStart" #touchend="handleEnd" #touchmove="handleMove" #dblclick="zoom" #wheel="zoomWheel" alt="360 Image" />
</div>
</script>
Does anyone know how I can fix this?
Edit
Below is the updated code with the changes suggested by the accepted answer, only issue now is the viewport square going out of frame.
Vue.component('test', {
template: '#template',
data: function() {
return {
loading: true,
loop: true,
speed: 8,
speedController: 0,
zoomEnabled: true,
zoomLevels: [1, 1.5, 2, 2.5, 3],
zoomLevel: 1,
frame: 1,
images: [],
imagesPreloaded: 0,
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,
};
},
mounted() {
window.addEventListener('mouseup', this.handleEnd);
window.addEventListener('touchend', this.handleEnd);
},
beforeDestroy() {
window.removeEventListener('mouseup', this.handleEnd);
window.removeEventListener('touchend', this.handleEnd);
},
methods: {
zoom(direction) {
// todo: Load high res image based on zoom level
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
if (this.zoomLevels[this.zoomLevels.indexOf(closest) + direction] === undefined) {
return;
}
let current = this.zoomLevels.indexOf(closest);
let index = current += direction;
if (direction === 0) {
index = 0;
}
this.zoomLevel = this.zoomLevels[index];
this.translate(null, this.$refs.image, true);
},
zoomWheel($event) {
$event.preventDefault();
this.zoomLevel += $event.deltaY * -0.01;
if (this.zoomLevel < 1) {
this.zoomLevel = 1;
}
let maxZoom = this.zoomLevels[this.zoomLevels.length - 1];
this.zoomLevel = Math.min(Math.max(.125, this.zoomLevel), maxZoom);
this.translate(null, this.$refs.image, true);
},
handleStart($event) {
$event.preventDefault();
if ($event.button && $event.button !== 0) {
return;
}
this.isMoving = true;
this.isDragging = true;
this.startX = $event.pageX || $event.touches[0].pageX;
this.startY = $event.pageY || $event.touches[0].pageY;
},
handleMove($event, viewport) {
if ($event.button && $event.button !== 0) {
return;
}
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, $event.target, null, viewport);
}
}
},
handleEnd($event) {
if ($event.button && $event.button !== 0) {
return;
}
this.isMoving = false;
},
translate(positions, element, zooming, viewport) {
window.requestAnimationFrame(() => {
positions = positions || {
x: this.startX,
y: this.startY
};
if (viewport) {
this._translateFromViewport(positions, element);
} else {
this._translateFromImage(positions, element, zooming);
}
this.startX = positions.x;
this.startY = positions.y;
});
},
_translateFromViewport: function(positions, element) {
let move = {
x: positions.x - this.startX,
y: positions.y - this.startY
};
let box = element.getBoundingClientRect();
let container = element.parentNode.getBoundingClientRect();
let translate = {
left: (container.left - box.left) - ((move.x * this.viewportScale) * this.zoomLevel),
right: (container.right - box.right) - ((move.x * this.viewportScale) * this.zoomLevel),
top: (container.top - box.top) - ((move.y * this.viewportScale) * this.zoomLevel),
bottom: (container.bottom - box.bottom) - ((move.y * this.viewportScale) * this.zoomLevel)
};
if (translate.left <= 0 && translate.right >= 0) {
this.translateX -= move.x / this.viewportScale;
}
if (translate.top <= 0 && translate.bottom >= 0) {
this.translateY -= move.y / this.viewportScale
}
},
_translateFromImage: function(positions, element, zooming) {
let move = {
x: Math.floor(positions.x - this.startX),
y: Math.floor(positions.y - this.startY)
};
let image = element.getBoundingClientRect();
let container = element.parentNode.getBoundingClientRect();
let translate = {
left: (container.left - image.left) - (move.x * this.zoomLevel),
right: (container.right - image.right) - (move.x * this.zoomLevel),
top: (container.top - image.top) - (move.y * this.zoomLevel),
bottom: (container.bottom - image.bottom) - (move.y * this.zoomLevel)
};
if (zooming) {
if (translate.left <= 0) {
this.translateX += translate.left;
}
if (translate.right >= 0) {
this.translateX += translate.right;
}
if (translate.top <= 0) {
this.translateY += translate.top;
}
if (translate.bottom >= 0) {
this.translateY += translate.bottom;
}
}
if (translate.left >= 0 && translate.right <= 0) {
this.translateX += move.x / this.zoomLevel;
}
if (translate.top >= 0 && translate.bottom <= 0) {
this.translateY += move.y / this.zoomLevel;
}
},
},
computed: {
preloadProgress: function() {
return Math.floor(this.imagesPreloaded / this.images.length * 100);
},
currentPath: function() {
return this.images[this.frame - 1];
},
nextZoomLevel: function() {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
if (this.zoomLevels.indexOf(closest) === this.zoomLevels.length - 1) {
return this.zoomLevels[0];
}
return this.zoomLevels[this.zoomLevels.indexOf(closest) + 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() {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
return this.zoomLevels[this.zoomLevels.indexOf(closest) + 1] === undefined
},
canZoomOut: function() {
const closest = this.zoomLevels.reduce((a, b) => {
return Math.abs(b - this.zoomLevel) < Math.abs(a - this.zoomLevel) ? b : a;
});
return this.zoomLevels[this.zoomLevels.indexOf(closest) + -1] === undefined
}
}
});
window.vue = new Vue({
el: '#app'
});
.media-360-viewer {
position: relative;
overflow: hidden;
background: #000;
width: 500px;
}
.media-360-viewer__image {
width: 100%;
}
.media-360-viewer__image.canTranslate {
cursor: grab;
}
.media-360-viewer__image.isTranslating {
cursor: grabbing;
}
.media-360-viewer__image.canRotate {
cursor: w-resize;
}
.media-360-viewer__loader {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
}
.media-360-viewer__loader * {
user-select: none;
}
.media-360-viewer__loader > svg {
width: 100%;
height: 100%;
transform: rotate(270deg);
}
.media-360-viewer__loader--text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.media-360-viewer__loader--text p {
font-size: 100%;
font-weight: bold;
color: #fff;
}
.media-360-viewer__loader--text p.large {
font-size: 150%;
}
.media-360-viewer__loader--background {
stroke-dasharray: 0;
stroke-dashoffset: 0;
stroke: rgba(0, 0, 0, 0.7);
stroke-width: 25px;
}
.media-360-viewer__loader--cover {
stroke-dasharray: 200%;
stroke: #848484;
stroke-width: 15px;
stroke-linecap: round;
}
.media-360-viewer__loader--background,
.media-360-viewer__loader--cover {
fill: transparent;
}
.media-360-viewer__viewport {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
overflow: hidden;
}
.media-360-viewer__viewport--image {
width: 100%;
pointer-events: none;
}
.media-360-viewer__viewport--zoom {
position: absolute;
bottom: 5px;
right: 5px;
color: #fff;
z-index: 3;
font-size: 12px;
pointer-events: none;
}
.media-360-viewer__viewport--square {
display: block;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
box-shadow: rgba(0, 0, 0, 0.8) 0 0 0 10000px;
cursor: grab;
transition: background ease-in-out 0.1s;
}
.media-360-viewer__viewport--square:hover {
background: rgba(255, 255, 255, 0.2);
}
.media-360-viewer__tools {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 10px;
}
.media-360-viewer__tools > a {
margin: 0 5px;
color: #000;
background: #fff;
border-radius: 50%;
width: 40px;
text-align: center;
line-height: 40px;
}
.media-360-viewer__tools > a[disabled] {
opacity: .5;
cursor: not-allowed;
}
.media-360-viewer__tools > a[disabled]:hover {
color: #000;
background: #fff;
}
.media-360-viewer__tools > a:hover {
background: #000;
color: #fff;
}
.media-360-viewer__tools--autoplay:before {
font-family: 'ClickIcons';
content: '\ea81';
}
.media-360-viewer__tools--autoplay.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></test>
</div>
<script type="text/x-template" id="template">
<div class="media-360-viewer" ref="container">
<transition name="fade">
<div class="media-360-viewer__viewport" v-if="zoomLevel > 1 && viewportEnabled" :style="{ width: (viewportScale * 100) + '%' }">
<img tabindex="1" draggable="false" alt="Viewport" class="media-360-viewer__viewport--image" src="https://www.bennetts.co.uk/-/media/bikesocial/2019-september-images/2020-yamaha-yzf-r1-and-r1m-review/2020-yamaha-r1-and-r1m_005.ashx?h=493&w=740&la=en&hash=F97CD240F0DDFA9540E912DCF7F07019017035C6">
<span class="media-360-viewer__viewport--zoom">
x{{ Math.round(zoomLevel * 10) / 10 }}
</span>
<span :style="{ transform: viewportTransform }" #mouseup="handleEnd" #mousedown="handleStart" #mousemove="handleMove($event, true)" #touchstart="handleStart" #touchend="handleEnd" #touchmove="handleMove($event, true)" class="media-360-viewer__viewport--square"></span>
</div>
</transition>
<img tabindex="1" ref="image" draggable="false" src="https://www.bennetts.co.uk/-/media/bikesocial/2019-september-images/2020-yamaha-yzf-r1-and-r1m-review/2020-yamaha-r1-and-r1m_005.ashx?h=493&w=740&la=en&hash=F97CD240F0DDFA9540E912DCF7F07019017035C6"
:style="{ transform: transform }" :class="{
canTranslate: zoomLevel > 1 && zoomEnabled,
canRotate: zoomLevel === 1,
isTranslating: zoomLevel > 1 && zoomEnabled && isMoving
}" #mouseup="handleEnd" #mousedown="handleStart" #mousemove="handleMove" #touchstart="handleStart" #touchend="handleEnd" #touchmove="handleMove" #dblclick="zoom" #wheel="zoomWheel" alt="360 Image" />
</div>
</script>
Math.floor only when necessary
A part of this problem is a result of calling Math.floor all the time. Each time you call Math.floor your next calculations will be less accurate.
If you still want to round numbers, do it only at the end of your calculations chain or even in the place where you are using the variable. For example:
transform: function() {
const translateX = Math.floor(this.translateX)
const translateY = Math.floor(this.translateY)
return `scale(${this.zoomLevel}) translateX(${translateX}px) translateY(${translateY}px)`;
}
Scaling factor
However when moving the image around it is moving faster than the mouse
This can be fixed by dividing number you're adding to the translate value by scale factor, which is determined by zoomLevel like so:
if (translate.left >= 0 && translate.right <= 0) {
this.translateX += move.x / this.zoomLevel
}
if (translate.top >= 0 && translate.bottom <= 0) {
this.translateY += move.y / this.zoomLevel
}
Scaling factor - viewport
Viewport is still somehow broken too because of scaling, we need to adjust translation value too, but this time not using zoomLevel but scale of the viewport which is stored in this.viewportScale. So, merging two of solutions together we now have code like this:
if (translate.left >= 0 && translate.right <= 0) {
if (viewport) {
this.translateX += move.x / this.viewportScale
} else {
this.translateX += move.x / this.zoomLevel
}
}
if (translate.top >= 0 && translate.bottom <= 0) {
if (viewport) {
this.translateY += move.y / this.viewportScale
} else {
this.translateY += move.y / this.zoomLevel
}
}
Reversing values
I also feel like when i'm clicking and dragging on the viewport I shouldn't be reversing the values twice
I don't think that there is a better way to do it unless you want to make two inner translate functions that will handle translation depending on the input source (viewport or image). It will be definetely cleaner approach to coding. For example if in future you would like to write some viewport specific code you will not need to include another and another if. Your function could like like so:
translate (positions, element, zooming, viewport) {
positions = positions || {
x: this.startX,
y: this.startY
}
if (viewport) {
this._translateFromViewport(positions, element, zooming)
} else {
this._translateFromImage(positions, element, zooming)
}
this.startX = positions.x
this.startY = positions.y
}
where _translateFromViewport and _translateFromImage functions hold feature specific code.

Categories