There's a div (brown rectangle) on the page. The page is higher than the viewport (orange rectangle) so it can be scrolled, which means that the div might only partially show up or not at all.
What's the simplest algorithm to tell how much % of the div is visible in the viewport?
(To make things easier, the div always fits into the viewport horizontally, so only the Y axis needs to be considered at the calculations.)
See one more example in fiddle:
https://jsfiddle.net/1hfxom6h/3/
/*jslint browser: true*/
/*global jQuery, window, document*/
(function ($) {
'use strict';
var results = {};
function display() {
var resultString = '';
$.each(results, function (key) {
resultString += '(' + key + ': ' + Math.round(results[key]) + '%)';
});
$('p').text(resultString);
}
function calculateVisibilityForDiv(div$) {
var windowHeight = $(window).height(),
docScroll = $(document).scrollTop(),
divPosition = div$.offset().top,
divHeight = div$.height(),
hiddenBefore = docScroll - divPosition,
hiddenAfter = (divPosition + divHeight) - (docScroll + windowHeight);
if ((docScroll > divPosition + divHeight) || (divPosition > docScroll + windowHeight)) {
return 0;
} else {
var result = 100;
if (hiddenBefore > 0) {
result -= (hiddenBefore * 100) / divHeight;
}
if (hiddenAfter > 0) {
result -= (hiddenAfter * 100) / divHeight;
}
return result;
}
}
function calculateAndDisplayForAllDivs() {
$('div').each(function () {
var div$ = $(this);
results[div$.attr('id')] = calculateVisibilityForDiv(div$);
});
display();
}
$(document).scroll(function () {
calculateAndDisplayForAllDivs();
});
$(document).ready(function () {
calculateAndDisplayForAllDivs();
});
}(jQuery));
div {
height:200px;
width:300px;
border-width:1px;
border-style:solid;
}
p {
position: fixed;
left:320px;
top:4px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="div1">div1</div>
<div id="div2">div2</div>
<div id="div3">div3</div>
<div id="div4">div4</div>
<p id="result"></p>
Here's a snippet illustrating how you can calculate this.
I've put the % values in the boxes for readability, and it even kinda "follows" the viewport ^^ :
Fiddle version
function listVisibleBoxes() {
var results = [];
$("section").each(function () {
var screenTop = document.documentElement.scrollTop;
var screenBottom = document.documentElement.scrollTop + $(window).height();
var boxTop = $(this).offset().top;
var boxHeight = $(this).height();
var boxBottom = boxTop + boxHeight;
if(boxTop > screenTop) {
if(boxBottom < screenBottom) {
//full box
results.push(this.id + "-100%");
$(this).html("100%").css({ "line-height": "50vh" });
} else if(boxTop < screenBottom) {
//partial (bottom)
var percent = Math.round((screenBottom - boxTop) / boxHeight * 100) + "%";
var lineHeight = Math.round((screenBottom - boxTop) / boxHeight * 50) + "vh";
results.push(this.id + "-" + percent);
$(this).html(percent).css({ "line-height": lineHeight });
}
} else if(boxBottom > screenTop) {
//partial (top)
var percent = Math.round((boxBottom - screenTop) / boxHeight * 100) + "%";
var lineHeight = 100 - Math.round((boxBottom - screenTop) / boxHeight * 50) + "vh";
results.push(this.id + "-" + percent);
$(this).html(percent).css({ "line-height": lineHeight });
}
});
$("#data").html(results.join(" | "));
}
$(function () {
listVisibleBoxes();
$(window).on("scroll", function() {
listVisibleBoxes();
});
});
body {
background-color: rgba(255, 191, 127, 1);
font-family: Arial, sans-serif;
}
section {
background-color: rgba(175, 153, 131, 1);
height: 50vh;
font-size: 5vh;
line-height: 50vh;
margin: 10vh auto;
overflow: hidden;
text-align: center;
width: 50vw;
}
#data {
background-color: rgba(255, 255, 255, .5);
left: 0;
padding: .5em;
position: fixed;
top: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section id="one"></section>
<section id="two"></section>
<section id="three"></section>
<section id="four"></section>
<section id="five"></section>
<section id="six"></section>
<div id="data">data here</div>
After playing around a bit I think I've found perhaps the simplest way to do it: I basically determine how much the element extends over the viewport (doesn't matter in which direction) and based on this it can easily be calculated how much of it is visible.
// When the page is completely loaded.
$(document).ready(function() {
// Returns in percentages how much can be seen vertically
// of an element in the current viewport.
$.fn.pvisible = function() {
var eTop = this.offset().top;
var eBottom = eTop + this.height();
var wTop = $(window).scrollTop();
var wBottom = wTop + $(window).height();
var totalH = Math.max(eBottom, wBottom) - Math.min(eTop, wTop);
var wComp = totalH - $(window).height();
var eIn = this.height() - wComp;
return (eIn <= 0 ? 0 : eIn / this.height() * 100);
}
// If the page is scrolled.
$(window).scroll(function() {
// Setting the opacity of the divs.
$("div").each(function() {
$(this).css("opacity", Math.round($(this).pvisible()) / 100);
});
});
});
html,
body {
width: 100%;
height: 100%;
}
body {
background-color: rgba(255, 191, 127, 1);
}
div {
width: 60%;
height: 30%;
margin: 5% auto;
background-color: rgba(175, 153, 131, 1);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
A little illustration to help understand how it works:
Chrome now supports Intersection Observer API
Example (TypeScript):
export const elementVisibleInPercent = (element: HTMLElement) => {
return new Promise((resolve, reject) => {
const observer = new IntersectionObserver((entries: IntersectionObserverEntry[]) => {
entries.forEach((entry: IntersectionObserverEntry) => {
resolve(Math.floor(entry.intersectionRatio * 100));
clearTimeout(timeout);
observer.disconnect();
});
});
observer.observe(element);
// Probably not needed, but in case something goes wrong.
const timeout = setTimeout(() => {
reject();
}, 500);
});
};
const example = document.getElementById('example');
const percentageVisible = elementVisibleInPercent(example);
Example (JavaScript):
export const elementVisibleInPercent = (element) => {
return new Promise((resolve, reject) => {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
resolve(Math.floor(entry.intersectionRatio * 100));
clearTimeout(timeout);
observer.disconnect();
});
});
observer.observe(element);
// Probably not needed, but in case something goes wrong.
const timeout = setTimeout(() => {
reject();
}, 500);
});
};
const example = document.getElementById('example');
const percentageVisible = elementVisibleInPercent(example);
Please note that the Intersection Observer API is available since then, made specifically for this purpose.
Related
I am trying to develop a very simple game where the ship (red box) will move left-right when user clicks on playground.
There are some moving walls (black boxes) as obstacles that the ship should avoid colliding with.
If any collision happens, the walls will stop moving and a text will be printed out in console.
I have succeeded to get this as close as I can. But its working sometime, not always. You can see it in the code below, try to collide with wall. Sometime it will stop them and print text, sometime it will just ignore the collision as if nothing happens.
I have no clue why this is happening.
Here is the code.
$('document').ready(function() {
var $totalHeight = $('.inner').height(); //of walls
var $maxHeight = Math.ceil(Math.ceil($totalHeight / 3) - (Math.ceil($totalHeight / 3) * 30) / 100); //30% of total wall height
$('.wall').each(function(i, obj) {
$(this).height($maxHeight);
$('.wall.four').css({
'height': $wallGap
});
})
var $wallGap = Math.ceil($totalHeight / 3) - $maxHeight;
var $wallOneTop = 0;
var $wallTwoTop = $maxHeight + $wallGap;
var $wallThreeTop = ($maxHeight * 2) + ($wallGap * 2);
var $wallFourTop = -$('.wall.four').height() - $wallGap;
$('.wall.one').css({
'top': $wallOneTop
});
$('.wall.two').css({
'top': $wallTwoTop
});
$('.wall.three').css({
'top': $wallThreeTop
});
$('.wall.four').css({
'top': $wallFourTop
});
function moveWall(wallObj) {
var $currentTop = wallObj.position().top;
var $limitTop = $('.inner').height();
if ($currentTop >= $limitTop) {
var $rand = Math.floor(Math.random() * ($maxHeight - $wallGap + 1) + $wallGap);
wallObj.height($rand);
var $top = -(wallObj.height());
} else {
var $top = (wallObj.position().top) + 5;
}
var $collide = checkCollision(wallObj);
wallObj.css({
'top': $top
});
return $collide;
}
var $wallTimer = setInterval(function() {
$('.wall').each(function(i, obj) {
var $status = moveWall($(this));
if ($status == true) {
clearInterval($wallTimer);
}
})
}, 40);
function checkCollision(wallObj) {
var $ship = $('.ship');
var $shipWidth = $ship.width();
var $shipHeight = $ship.height();
var $shipLeft = $ship.position().left;
var $shipRight = $shipLeft + $shipWidth;
var $shipTop = $ship.position().top;
var $shipBottom = $shipTop + $shipHeight;
var $wall = wallObj;
var $wallWidth = wallObj.width();
var $wallHeight = wallObj.height();
var $wallLeft = wallObj.position().left;
var $wallRight = $wallLeft + $wallWidth;
var $wallTop = wallObj.position().top;
var $wallBottom = $wallTop + $wallHeight;
if (
$shipLeft >= $wallRight ||
$shipRight <= $wallLeft ||
$shipTop >= $wallBottom ||
$shipBottom <= $wallTop
) {
return false;
} else {
console.log("dhumm!");
return true;
}
}
$('.outer .inner').click(function() {
var $ship;
$ship = $('.ship');
$shipLeft = $ship.position().left;
$shipRight = $shipLeft + $ship.width();
$inner = $('.inner');
$innerLeft = $inner.position().left;
$innerRight = $innerLeft + $inner.width();
if (($shipLeft < $inner.width() - $ship.width())) {
$ship.animate({
"left": $inner.width() - $ship.width()
}, 500, "linear");
} else if (($shipRight >= $inner.width())) {
$ship.animate({
"left": '0'
}, 500, "linear");
}
});
});
.outer {
background: #fff;
border: 20px solid #efefef;
width: 400px;
height: 600px;
display: inline-block;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
overflow: hidden;
}
.outer .inner {
background: #fff;
height: 100%;
width: 100%;
margin: auto;
position: relative;
overflow: hidden;
}
.outer .inner .wall {
width: 5px;
position: absolute;
left: 50%;
transform: translateX(-50%);
background: #000;
}
.outer .inner .ship {
width: 15px;
height: 15px;
background: red;
position: absolute;
top: 50%;
transform: translateY(-50%);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="outer">
<div class="inner">
<div class="wall one"></div>
<div class="wall two"></div>
<div class="wall three"></div>
<div class="wall four"></div>
<div class="ship"></div>
</div>
</div>
As freefomn-m already said.
Check for collision in the animation cycle of the ship, not the walls.
For this I use the second type of parameters for jQuery's .animate method
.animate( properties, options )
I use the "progress" option to check the collision in every movement cycle of the ship.
console.clear();
$('document').ready(function() {
var collided = false;
var collidedWith = null;
var $ship = $('.ship');
var $walls = $('.wall')
var $totalHeight = $('.inner').height(); //of walls
var $maxHeight = Math.ceil(Math.ceil($totalHeight / 3) - (Math.ceil($totalHeight / 3) * 30) / 100); //30% of total wall height
$('.wall').each(function(i, obj) {
$(this).height($maxHeight);
$('.wall.four').css({
'height': $wallGap
});
})
var $wallGap = Math.ceil($totalHeight / 3) - $maxHeight;
var $wallOneTop = 0;
var $wallTwoTop = $maxHeight + $wallGap;
var $wallThreeTop = ($maxHeight * 2) + ($wallGap * 2);
var $wallFourTop = -$('.wall.four').height() - $wallGap;
$('.wall.one').css({
'top': $wallOneTop
});
$('.wall.two').css({
'top': $wallTwoTop
});
$('.wall.three').css({
'top': $wallThreeTop
});
$('.wall.four').css({
'top': $wallFourTop
});
function moveWall(wallObj) {
var $currentTop = wallObj.position().top;
var $limitTop = $('.inner').height();
if ($currentTop >= $limitTop) {
var $rand = Math.floor(Math.random() * ($maxHeight - $wallGap + 1) + $wallGap);
wallObj.height($rand);
var $top = -(wallObj.height());
} else {
var $top = (wallObj.position().top) + 5;
}
// var $collide = checkCollision(wallObj);
wallObj.css({
'top': $top
});
// return $collide;
}
var $wallTimer = setInterval(function() {
$walls.each(function(i, obj) {
moveWall($(this));
if (collided) {
clearInterval($wallTimer);
}
})
}, 40);
function checkCollision() {
var $shipWidth = $ship.width();
var $shipHeight = $ship.height();
var $shipLeft = $ship.position().left;
var $shipRight = $shipLeft + $shipWidth;
var $shipTop = $ship.position().top;
var $shipBottom = $shipTop + $shipHeight;
$('.wall').each(function(i) {
var $wall = $(this);
var $wallWidth = $wall.width();
var $wallHeight = $wall.height();
var $wallLeft = $wall.position().left;
var $wallRight = $wallLeft + $wallWidth;
var $wallTop = $wall.position().top;
var $wallBottom = $wallTop + $wallHeight;
if (
$shipLeft < $wallRight &&
$shipRight > $wallLeft &&
$shipTop < $wallBottom &&
$shipBottom > $wallTop
) {
console.log("dhumm!");
collided = true;
collidedWith = $wall
$wall.addClass('crashed')
$ship.addClass('crashed')
$ship.stop();
return false;
}
})
}
$('.outer .inner').click(function() {
var $ship;
$ship = $('.ship');
$shipLeft = $ship.position().left;
$shipRight = $shipLeft + $ship.width();
$inner = $('.inner');
$innerLeft = $inner.position().left;
$innerRight = $innerLeft + $inner.width();
if (($shipLeft < $inner.width() - $ship.width())) {
$ship.animate({
"left": $inner.width() - $ship.width()
}, {
"duration": 500,
"easing": "linear",
"progress": checkCollision,
});
} else if (($shipRight >= $inner.width())) {
$ship.animate({
"left": '0'
}, {
"duration": 500,
"easing": "linear",
"progress": checkCollision,
});
}
});
});
.outer {
background: #fff;
border: 20px solid #efefef;
width: 400px;
height: 600px;
display: inline-block;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
overflow: hidden;
}
.outer .inner {
background: #fff;
height: 100%;
width: 100%;
margin: auto;
position: relative;
overflow: hidden;
}
.outer .inner .wall {
width: 5px;
position: absolute;
left: 50%;
transform: translateX(-50%);
background: #000;
}
.outer .inner .wall.crashed {
background: red;
}
.outer .inner .ship {
width: 15px;
height: 15px;
background: orange;
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.outer .inner .ship.crashed {
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="outer">
<div class="inner">
<div class="wall one"></div>
<div class="wall two"></div>
<div class="wall three"></div>
<div class="wall four"></div>
<div class="ship"></div>
</div>
</div>
As a recommendation how I would do this from scratch.
Use an update cycle that is called by either setInterval or setTimeout, or even better with requestAnimationFrame. The updatecycle would be responsible for the time progress and orchestrate the different objects. The structure would be like this.
jQuery(function($) { // same as $('document').ready()
var ship = ...;
var boundaries = ...;
var walls = ...;
var clickEvents = [];
document.addEventListener('click', function(e) {clickEvents.push(e)})
var handleEvents = function() {}
var setupWalls = function () {}
var setupShip= function () {}
var moveWalls = function () {}
var moveShip = function () {}
var checkCollision() {}
var setup = function() {
setupWalls();
setupShip();
// set the initial positions of the ships and the walls
}
var update = function() {
handleEvents();
moveWalls();
moveShips();
var collided = checkCollision();
if (!collided) {
setTimeout(update, 30);
}
}
setup();
update();
})
I wanna do some effect to my background so i tought i can add some shapes flying around but i do only 1 shape i cant loop or anything
i tried call function more than a one time but it doesnt help
function animasyon(a) {
window.onload=function(){
var id = setInterval(anim,5);
$('body').append('<div class=shape></div>');
$('body').append('<div class=shape></div>');
var kutu = document.getElementsByClassName("shape");
var pos = 0;
var x = window.innerWidth;
var y = window.innerHeight;
var borderSize = Math.floor(Math.random() * 3 ) + 1;
var ySize = Math.floor(Math.random() * y ) + 1;
var Size = Math.floor(Math.random() * 30 ) + 5;
var yon = Math.floor(Math.random() *2)+1;
var dolu = Math.floor(Math.random() *2)+1;
if (ySize > 50) { ySize-=20; }
function anim(){
if (pos == x) {
clearInterval(id);
document.getElementById("shape").remove();
}else{
pos++;
kutu[a].style.position = "absolute";
kutu[a].style.border = "solid rgb(119,38,53) "+borderSize+"px";
kutu[a].style.left = pos+"px";
kutu[a].style.width = Size+"px";
kutu[a].style.height = Size+"px";
if (yon == 1) { ySize-=0.2; } else { ySize+=0.2; }
if (dolu==1) {kutu[a].style.background = "rgb(119,38,53)";}
if (kutu[a].offsetTop < 0 || kutu[a].offsetTop > y-30) {document.getElementById("shape").remove();}
kutu[a].style.top = ySize+"px";
}
}
}
}
animasyon(0);
Try Calling 'anim' function in this way
setInterval(function(){anim()},5);
Your problem is that you are assigning window.onload function a value inside the function animasyon
window.onload holds only 1 function. If you call animation function more than once, then the last one will overwrite the first one.
Edit: You need to separate your animation logic from the page load logic. They are completely separate things.
Here is an example of adding multiple objects and animating each separately.
// HTML
<div id="container">
<div id="ball"></div>
<div id="ball2"></div>
<div id="ball3"></div>
<div id="ball4"></div>
</div>
// CSS
#ball, #ball2, #ball3, #ball4 {
background: red;
border: 1px solid #FAFDFA;
display: inline-block;
width: 1em;
height: 1em;
border-radius: 2em;
position: absolute;
}
#ball2 {
background: blue;
}
#ball3 {
background: green;
}
#ball4 {
background: purple;
}
#container {
width: 512px;
height: 512px;
background: black;
position: relative;
}
// JS
const container = document.getElementById('container');
const stageWidth = 512;
const stageHeight = 512;
const makeFall = (elementId) => {
// this function makes an enlement fall in random direction
const animationSpeed = 4;
// your onload function
const ball = document.getElementById(elementId);
let directionX = (Math.random() * animationSpeed * 2) - animationSpeed;
let directionY = Math.random() * animationSpeed;
const setRandomStart = () => {
ball.style.top = '10px';
ball.style.left = (Math.random() * (stageWidth / 2)) + 'px';
directionX = (Math.random() * animationSpeed * 2) - animationSpeed;
directionY = Math.random() * animationSpeed;
}
setRandomStart();
let animationInterval = setInterval(() => {
let px = parseFloat(ball.style.left);
let py = parseFloat(ball.style.top);
px += directionX;
py += directionY;
if (px > stageWidth - 20 || py > stageHeight - 20 || px < -20) {
setRandomStart();
} else {
ball.style.left = px + 'px';
ball.style.top = py + 'px';
}
}, 48);
}
// In Your onload function you can add the elements and then animate
makeFall('ball');
makeFall('ball2');
makeFall('ball3');
makeFall('ball4');
https://jsfiddle.net/753oL8re/4/
I have written my first piece of code that I feel could actually be reusable and possibly useful for others. The code is a bit of css, a few javascript-classes and some js-functions.
To use the package, you set up some HTML, and then pass a html-element and an optional parameter, for example like this:
dom = new DOM(document.getElementById('fullScreenWrapper'), 175),
I would like to pack it in some way so that you basically include a js-file, a css-file and then you can use it in any project. jQuery has a design-pattern for extending itself, but I don't use jQuery. Is there any best practice for stuff like this?
Here is the mockup, not packaged:
<html>
<head>
<style>
/* specific styles */
&:root {--scrollPosition:0}
.flicWrapper{
height: calc(var(--wrapperHeight));
}
.flicWrapper .flicChild{
top: calc(var(--previousHeight));
position: fixed;
}
.flicWrapper.transition{
height: initial;
}
.flicWrapper.transition .flicChild{
transition: transform .3s cubic-bezier(0, 0, 0.25, 1);
}
.flicWrapper .before{
transform: translate3d(0,calc((var(--previousHeight)*-1)),0);
}
.flicWrapper .active{
transform: translate3d(0, calc((var(--previousHeight)*-1) + var(--stickyScrollPosition)), 0);
}
.flicWrapper .next{
transform: translate3d(0, calc(var(--scrollPosition)), 0);
}
/*custom style*/
body{
margin: 0;
}
section{
width: 100%;
height: 100%;
}
.s1{
height: 100%;
background: repeating-linear-gradient(
45deg,
red,
red 10px,
blue 10px,
blue 20px
);
}
.s2{
height: 150%;
background: repeating-linear-gradient(
45deg,
#606dbc,
#606dbc 10px,
#465298 10px,
#465298 20px
);
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<div class="fullScreenWrapper" id="fullScreenWrapper">
<section class="s1" ></section>
<section class="s2"></section>
<section class="s3" style="background: red";></section>
<section class="s4" style="background: green";></section>
<section class="s5" style="background: black";></section>
</div>
<script type="text/javascript">
var State = class {
constructor(heights, offset) {
this.breakingPoints = heights;
this.offset = offset;
this.state = this.currentState;
}
get currentState(){
return this.breakingPoints.findIndex((elm, i, arr)=> {
if(window.scrollY >= arr[i] && window.scrollY < arr[i+1] ){
return[i]
}
})
}
get update(){
var isUpdated = Math.sign( this.currentState - this.state)
this.state += isUpdated;
return isUpdated !== 0
}
get snapPos() {
var offset = this.state ? this.offset : 0; // first state should not have offset.
return this.breakingPoints[this.state] + offset;
}
},
DOM = class {
constructor(wrapper, offset) {
this.wrapper = wrapper;
this.offset = offset
this.children = Array.from(wrapper.children);
this.childHeights = [];
this.scrollHeights = this.getScrollHeights;
this.wrapper.classList.add('flicWrapper')
setGlobalProperty('wrapperHeight', this.documentHeight)
//apply custom css-values
this.children.forEach((child, i) => {
child.classList.add('flicChild')
var height = 0
if(i > 0 ){
var height = this.children[i-1].offsetHeight;
}
child.style.setProperty('--previousHeight', height+'px');
this.childHeights.push(child.offsetHeight - window.innerHeight);
})
}
get getScrollHeights() {
var heights = [0];
//calculate al the heights
this.children.forEach((el, i) => {
var mod = 2;
if(i === 0 || i === this.children.length -1){mod = 1}; //first and last should only have one offset
var elheight = el.offsetHeight - window.innerHeight + this.offset * mod;
heights.push( elheight + heights[i] );
})
return heights;
}
get documentHeight() {return this.scrollHeights.slice(-1)[0]+window.innerHeight-1}
},
Scroll = class{
constructor(){
this.on = true;
}
throttle(fn, wait){
var time = Date.now();
return () => {
if ((time + wait - Date.now()) < 0 && this.on) {
fn();
time = Date.now();
}
}
}
},
scrollEvent = () => {
if(state.update){ //check state
scroll.on = false;
dom.wrapper.classList.add('transition')
setClasses();
setScrollPos(0, dom.childHeights[state.state]);
setTimeout(changeDone, 1200);
}
},
setClasses = () => {
var s = state.state;
dom.children.forEach((child, i) => {
let classes = child.classList;
classes.toggle('before', i < s);
classes.toggle('after', i > s);
classes.toggle('active', i === s);
classes.toggle('previous', i === s-1);
classes.toggle('next', i === s+1);
})
},
changeDone = () => {
dom.wrapper.classList.remove('transition');
window.scrollTo(0, state.snapPos);
scroll.on = true;
},
//helper
setGlobalProperty = (name, value) => {
document.documentElement.style.setProperty('--'+name, (value+'px'))
},
setScrollPos = (pos, max) => {
setGlobalProperty('scrollPosition', -pos)
setGlobalProperty('stickyScrollPosition', (Math.min(pos, max)*-1))
},
dom = new DOM(document.getElementById('fullScreenWrapper'), 175),
state = new State(dom.scrollHeights, dom.offset),
scroll = new Scroll();
setClasses();
window.addEventListener('scroll', scroll.throttle(scrollEvent, 50));
window.addEventListener('scroll', scroll.throttle(() => {setScrollPos(window.scrollY - state.snapPos, dom.childHeights[state.state])}, 0))
</script>
</body>
ive been looking in about for this exact script for a while and i cant get it to work. Im looking to use a fade in effect from left to right word by word.
For example
<div class="box5">
<h1>Lorem ipsum dolor sit amet, ne mel vero impetus voluptatibus
</h1>
</div>
I want this to fade in then enough line to fade in word by word slightly later using a delay.
My current fade in works but it does it by the full container it looks like this
.reveal {
position: relative;
overflow: hidden;
}
/*initial - hidden*/
.reveal .reveal__cover {
position: absolute;
top: 0;
left: -250px;
height: 100%;
margin: 2px;
width: calc(100% + 250px);
}
.reveal .reveal__cover.reveal__uncovered {
position: absolute;
top: 0;
left: 100%;
height: 100%;
width: calc(100% + 250px);
margin: 2px;
transition: left 2500ms ease-out 0ms;
}
.reveal__cover-section {
height: 100%;
float: right;
/* NOTE: Background must match existing background */
/*background: #000;*/
background: #fff;
width: 2%;
}
.reveal__10 {
opacity: 0.1;
}
.reveal__20 {
opacity: 0.2;
}
.reveal__30 {
opacity: 0.3;
}
.reveal__40 {
opacity: 0.4;
}
.reveal__50 {
opacity: 0.5;
}
.reveal__60 {
opacity: 0.6;
}
.reveal__70 {
opacity: 0.7;
}
.reveal__80 {
opacity: 0.8;
}
.reveal__90 {
opacity: 0.9;
}
.reveal__100 {
opacity: 1;
width: 82%;
}
Then JS
function replaceAllInstances(source, search, replacement) {
var regex = new RegExp(search, "g");
var result = source.replace(regex, replacement);
return result;
}
$.fn.isOnScreen = function (x, y) {
if (x == null || typeof x == 'undefined')
x = 1;
if (y == null || typeof y == 'undefined')
y = 1;
var win = $(window);
var viewport = {
top: win.scrollTop(),
left: win.scrollLeft()
};
viewport.right = viewport.left + win.width();
viewport.bottom = viewport.top + win.height();
var height = this.outerHeight();
var width = this.outerWidth();
if (!width || !height) {
return false;
}
var bounds = this.offset();
bounds.right = bounds.left + width;
bounds.bottom = bounds.top + height;
var visible = (!(viewport.right < bounds.left || viewport.left > bounds.right || viewport.bottom < bounds.top || viewport.top > bounds.bottom));
if (!visible) {
return false;
}
var deltas = {
top: Math.min(1, (bounds.bottom - viewport.top) / height),
bottom: Math.min(1, (viewport.bottom - bounds.top) / height),
left: Math.min(1, (bounds.right - viewport.left) / width),
right: Math.min(1, (viewport.right - bounds.left) / width)
};
return (deltas.left * deltas.right) >= x && (deltas.top * deltas.bottom) >= y;
};
/*
* Init specified element so it can be gradually revealed.
*
* Limitations:
* Only works on backgrounds with a solid color.
*
* #param options = {
* id:'box3'
* ,background='#ffffff' //default
* ,delay='0' //default
* }
*
*/
$.fn.initReveal = function (options) {
console.log('-------------');
console.log('selector:' + this.selector);
var parent = $(this).parent();
//grab a copy of the contents, then remove it from DOM
var contents = $(this).clone();
$(this).empty();
var revealHtmlBlock = "<div class='reveal'> <div class='text reveal__inner reveal__inner-{class}'> </div> <div class='reveal__cover reveal__cover-{class}'> <div class='reveal__cover-section reveal__100'></div> <div class='reveal__cover-section reveal__90'></div> <div class='reveal__cover-section reveal__80'></div> <div class='reveal__cover-section reveal__70'></div> <div class='reveal__cover-section reveal__60'></div> <div class='reveal__cover-section reveal__50'></div> <div class='reveal__cover-section reveal__40'></div> <div class='reveal__cover-section reveal__30'></div> <div class='reveal__cover-section reveal__20'></div> <div class='reveal__cover-section reveal__10'></div> </div> </div>";
revealHtmlBlock = replaceAllInstances(revealHtmlBlock, "{class}", options.id);
$(revealHtmlBlock).appendTo(parent);
contents.appendTo($('.reveal__inner-' + options.id));
//handle options
//delay
if (options.delay === undefined) {
console.log('delay set to 0');
options.delay = 0; //set default
} else {
console.log('delay set to:' + options.delay);
}
var revealElementFunction = function (options) {
$(this).performReveal(options);
};
//background
if (options.background !== undefined) {
$('.reveal__cover-' + options.id + ' .reveal__cover-section').css({'background-color': options.background});
}
//trigger the reveal at the specified time, unless auto is present and set to false
if (options.auto === undefined || (options.auto !== undefined && options.auto)) {
setTimeout(function () {
console.log('call');
revealElementFunction(options);
}, options.delay);
}
//trigger on-visible
if (options.trigger !== undefined) {
var revealOnScreenIntervalIdMap = {};
function uncoverText() {
var onscreen = $('.reveal__inner-box4').isOnScreen();
if ($('.reveal__inner-' + options.id).isOnScreen()) {
$('.reveal__cover-' + options.id).addClass('reveal__uncovered');
revealOnScreenIntervalIdMap[options.id] = window.clearInterval(revealOnScreenIntervalIdMap[options.id]);
}
}
function showTextWhenVisible() {
revealOnScreenIntervalIdMap[options.id] = setInterval(uncoverText, 800);
}
showTextWhenVisible();
}
};
//--------------------
/*
* trigger options:
* immediately (on page load)
* on event, eg. onclick
* on becoming visible, after it scrolls into view, or is displayed after bein ghidden
*
* #param options = {
* id:'box3'
* }
*
*/
$.fn.performReveal = function (options) {
var _performReveal = function () {
$('.reveal__cover-' + options.id).addClass('reveal__uncovered');
};
//allow time for init code to complete
setTimeout(_performReveal, 250);
};
Main JS
jQuery(function () {
//Box 1: reveal immediately - on page load
//NOTE: id does refer to an element id, It is used to
// uniquely refer to the element to be revealed.
var options1 = {
id: 'box1',
background: '#008d35'
};
$('.box1').initReveal(options1);
//------------------------
//Box 2: reveal after specified delay
var options2 = {
id: 'box2'
, delay: 3000
, background: '#008d35'
};
$('.box2').initReveal(options2);
//------------------------
//Box 3: reveal on event - eg. onclick
var options3 = {
id: 'box3'
, auto: false
};
var box3 = $('.box3');
box3.initReveal(options3);
$('.btn-reveal').on('click', function () {
box3.performReveal(options3);
});
//------------------------
//Box 4: Reveal when element scrolls into the viewport
var options4 = {
id: 'box4'
, auto: false
, trigger: 'on-visible'
};
$('.box4').initReveal(options4);
});
//------------------------
//Box 5 reveal
var options5 = {
id: 'box5'
, delay: 2500 ,
background: '#008d35'
};
$('.box5').initReveal(options5);
does anyone have any idea how to make it work word by word and not line by line
Here's a simple approach that you can build on. It creates the needed spans and fades them in based on interval value you set.
var fadeInterval = 300
$('h1').html(function(_, txt){
var words= $.trim(txt).split(' ');
return '<span>' + words.join('</span> <span>') + '</span>';
}).find('span').each(function(idx, elem){
$(elem).delay(idx * fadeInterval).fadeIn();
});
DEMO
I have a small problem with loading and position one of my images. The smallest one. I'm using spacegallery script (http://www.eyecon.ro/spacegallery/).
It's loading good, but i want it to position it in the corner of bigger image, and when i load my page for the first time, it's not positioned, when i reload it - it's all fine, but the first loading is wrong.
Here are the screens:
It's the first load without refreshing the site:
first load http://derivativeofln.com/withoutload.png
Second and next loads:
second load http://derivativeofln.com/withload.png
My HTML:
<div id="myGallery" class="spacegallery">
<img class="imaz" src=http://derivativeofln.com/magnifier.jpg />
<img class="aaa" src=images/bw1.jpg alt="" atr1="bw1" />
<img class="aaa" src=images/bw2.jpg alt="" atr1="bw2" />
<img class="aaa" src=images/bw3.jpg alt="" atr1="bw3" />
</div>
MY CSS:
#myGallery0 {
width: 100%;
height: 300px;
}
#myGallery0 img {
border: 2px solid #52697E;
}
a.loading {
background: #fff url(../images/ajax_small.gif) no-repeat center;
}
.imaz {
z-index: 10000;
}
.imaz img{
position: absolute;
left:10px;
top:10px;
z-index: 10000;
width: 35px;
height: 35px;
}
My Jquery main script file: (the first positioning instructions are on the bottom, so feel free to scroll down : ))
(function($){
EYE.extend({
spacegallery: {
animated: false,
//position images
positionImages: function(el) {
var top = 0;
EYE.spacegallery.animated = false;
$(el)
.find('a')
.removeClass(el.spacegalleryCfg.loadingClass)
.end()
.find('img.aaa')
.removeAttr('height')
.each(function(nr){
var newWidth = this.spacegallery.origWidth - (this.spacegallery.origWidth - this.spacegallery.origWidth * el.spacegalleryCfg.minScale) * el.spacegalleryCfg.asins[nr];
$(this)
.css({
top: el.spacegalleryCfg.tops[nr] + 'px',
marginLeft: - parseInt((newWidth + el.spacegalleryCfg.border)/2, 10) + 'px',
opacity: 1 - el.spacegalleryCfg.asins[nr]
})
.attr('width', parseInt(newWidth));
this.spacegallery.next = el.spacegalleryCfg.asins[nr+1];
this.spacegallery.nextTop = el.spacegalleryCfg.tops[nr+1] - el.spacegalleryCfg.tops[nr];
this.spacegallery.origTop = el.spacegalleryCfg.tops[nr];
this.spacegallery.opacity = 1 - el.spacegalleryCfg.asins[nr];
this.spacegallery.increment = el.spacegalleryCfg.asins[nr] - this.spacegallery.next;
this.spacegallery.current = el.spacegalleryCfg.asins[nr];
this.spacegallery.width = newWidth;
})
},
//constructor
init: function(opt) {
opt = $.extend({}, EYE.spacegallery.defaults, opt||{});
return this.each(function(){
var el = this;
if ($(el).is('.spacegallery')) {
$('')
.appendTo(this)
.addClass(opt.loadingClass)
.bind('click', EYE.spacegallery.next);
el.spacegalleryCfg = opt;
var listunia, images2 = [], index;
listunia = el.getElementsByTagName("img");
for (index = 1; index < listunia.length; ++index) {
images2.push(listunia[index]);
}
el.spacegalleryCfg.images = images2.length;
el.spacegalleryCfg.loaded = 0;
el.spacegalleryCfg.asin = Math.asin(1);
el.spacegalleryCfg.asins = {};
el.spacegalleryCfg.tops = {};
el.spacegalleryCfg.increment = parseInt(el.spacegalleryCfg.perspective/el.spacegalleryCfg.images, 10);
var top = 0;
$('img.aaa', el)
.each(function(nr){
var imgEl = new Image();
var elImg = this;
el.spacegalleryCfg.asins[nr] = 1 - Math.asin((nr+1)/el.spacegalleryCfg.images)/el.spacegalleryCfg.asin;
top += el.spacegalleryCfg.increment - el.spacegalleryCfg.increment * el.spacegalleryCfg.asins[nr];
el.spacegalleryCfg.tops[nr] = top;
elImg.spacegallery = {};
imgEl.src = this.src;
if (imgEl.complete) {
el.spacegalleryCfg.loaded ++;
elImg.spacegallery.origWidth = imgEl.width;
elImg.spacegallery.origHeight = imgEl.height
} else {
imgEl.onload = function() {
el.spacegalleryCfg.loaded ++;
elImg.spacegallery.origWidth = imgEl.width;
elImg.spacegallery.origHeight = imgEl.height
if (el.spacegalleryCfg.loaded == el.spacegalleryCfg.images) {
EYE.spacegallery.positionImages(el);
}
};
}
});
el.spacegalleryCfg.asins[el.spacegalleryCfg.images] = el.spacegalleryCfg.asins[el.spacegalleryCfg.images - 1] * 1.3;
el.spacegalleryCfg.tops[el.spacegalleryCfg.images] = el.spacegalleryCfg.tops[el.spacegalleryCfg.images - 1] * 1.3;
if (el.spacegalleryCfg.loaded == el.spacegalleryCfg.images) {
if(el.spacegalleryCfg.images == 3){
$('img.imaz', this).css('top', '27px'); // HERE IS POSITIONING OF MY IMAGES, WHEN SCRIPT LOADS FOR THE FIRST TIME!
$('img.imaz', this).css('left', (($(el).find('img.aaa:last').width())/2 + 77) + 'px' );
}
else if(el.spacegalleryCfg.images==2){
$('img.imaz', this).css('top', '33px');
$('img.imaz', this).css('left', (($(el).find('img.aaa:last').width())/2 + 77) + 'px' ); // HERE IT ENDS:)
}
EYE.spacegallery.positionImages(el);
}
}
});
}
}
});
})(jQuery);
this seems to be a cache thing
try to specify image dimensions (width and height) on your html, because on first load (images not in cache) the browser only knows dimensions after the load and the positioning might not be correct.
in alternative you can run your positioning code when the document is loaded
$(document).ready(«your poisitioning function here»)
see http://api.jquery.com/ready/ for that jquery ready function