How to scroll to the next video on user scroll - javascript

i am building a video sharing app with ionic, a lot of videos on the page and each video is occupying almost all the height of the page, what i want to achieve is that when the user scrolls up, it will automatically place the next video at the bottom at video point of the screen and when the user scrolls down, it will automatically place the previous video at viewpoint of the screen.
Here is my code
the video.page.scss
the video.page.html
<div class="DivFyll DivVid" id="deyDiv_{{i}}" *ngFor="let video of videos; let i=index">
<video class="video" [autoplay]="i==0?true:false" [poster]="service.ApiURL+video.thumbs_path" #player playsinline [id]="i" loop preload="auto">
<source [src]="service.ApiURL+video.vid_path" type="video/mp4" />
</video>
</div>
the video.page.scss
ion-content {
overflow-y: auto;
width: 100%;
height: 100%;
display: block;
--background-color: transparent !important;
.DivFyll {
width: 100%;
height: 100%;
}
.DivVid {
position: relative;
}
video {
background-color: transparent;
width: 100%;
display: block;
height: 80%;
object-fit: fill;
margin-top: auto;
margin-bottom: auto;
}
}
Please does anyone knows how i can implement this with javascript or ionic

This Scroller Component Might Help:
Create Scroll Component Like this:
Create EventListener For Scroll, Then When User Scroll You get the event and with translate 3d or anything you want change the content of scroller component.
with ng-content you can embed any other component inside scroller component.
Html File:
<ng-content></ng-content>
TS File:
export class ScrollerComponent implements OnInit, OnDestroy {
#Output() scrollChange: EventEmitter<ScrollerEventArgs> = new EventEmitter();
#ViewChild('content') content: ElementRef;
scrollYPos: number = 0;
scrollXPos: number = 0;
prevScrollYPos: number = 0;
prevScrollXPos: number = 0;
element: HTMLElement;
scrollLength: number;
start: number;
end: number;
private previousStart: number;
private previousEnd: number;
private rowHeightCache: RowHeightCache = new RowHeightCache();
private scrollListener: any;
constructor(element: ElementRef, private ngZone: NgZone) {
this.element = element.nativeElement;
}
ngOnInit() {
this.ngZone.runOutsideAngular(() => {
this.scrollListener = this.onScrolled.bind(this);
this.element.addEventListener('scroll', this.scrollListener);
});
}
ngOnDestroy() {
this.element.removeEventListener('scroll', this.scrollListener);
}
onScrolled(event: MouseEvent) {
// debugger
const dom: Element = event.currentTarget as Element;
this.scrollYPos = dom.scrollTop;
this.scrollXPos = dom.scrollLeft;
let direction = null;
if (this.scrollYPos < this.prevScrollYPos) {
direction = 'up';
} else if (this.scrollYPos > this.prevScrollYPos) {
direction = 'down';
}
if (this.prevScrollYPos !== this.scrollYPos || this.prevScrollXPos !== this.scrollXPos) {
if (direction && this.virtualScroll) {
this.chunkRows();
let topPadding = this.rowHeight * this.start;
if (this.rowHeightProp) {
topPadding = this.rowHeightCache.getRowOffset(this.start - 1);
}
this.ngZone.runOutsideAngular(() => {
requestAnimationFrame(() => {
this.content.nativeElement.style.transform = `translateY(${topPadding}px)`;
});
});
}
this.scrollChange.emit({
direction,
scrollYPos: this.scrollYPos,
scrollXPos: this.scrollXPos
});
this.prevScrollYPos = this.scrollYPos;
this.prevScrollXPos = this.scrollXPos;
} else if (this.scrollXPos === 0 ) {
this.scrollChange.emit({
direction,
scrollYPos: this.scrollYPos,
scrollXPos: this.scrollXPos
});
}
}
}
Update: RowHeightCache Class Added
export class RowHeightCache {
private cache: number[] = [];
calcScrollLength(totalRecords: number) {
return this.cache[totalRecords - 1];
}
initCache(rows: any[], rowHeightProp: string) {
const size = rows.length;
this.cache = new Array(size);
for (let i = 0; i < size; ++i) {
this.cache[i] = 0;
}
rows.forEach((row, i) => {
for (let index = i; index < size; index++) {
this.cache[index] += row[rowHeightProp];
}
});
}
calcRowIndex(offsetY: number): number {
if (offsetY === 0) {
return 0;
}
let pos = -1;
const dataLength = this.cache.length;
for (let i = dataLength; i >= 0; i--) {
const nextPos = pos + i;
if (nextPos < dataLength && offsetY >= this.cache[nextPos]) {
offsetY -= this.cache[nextPos];
pos = nextPos;
}
}
return pos + 1;
}
getRowOffset(rowIndex: number) {
if (rowIndex < 0) {
return 0;
}
return this.cache[rowIndex];
}
}

Related

I need to make a high-score list for this Javascript game

I hope you can give me a hand with this. My idea is to show a list of high-scores after the game is finished for a Doodle Jump project (javascript). The high-scores are presented successfully as you will see in my code, but the presentation is poor. Hence, I want to show them in a blank page, if possible using the same html. I will leave my code for you to reproduce the issue and help me. I thought about some document command, but you tell me.
Thanks in advance.
document.addEventListener('DOMContentLoaded', () => {
const grid = document.querySelector('.grid')
const doodler = document.createElement('div')
const unMutedIcon = document.createElement('div')
let doodlerLeftSpace = 50
let startPoint = 150
let doodlerBottomSpace = startPoint
let isGameOver = false
let platformCount = 5
let platforms = []
let upTimerid
let downTimerId
let isJumping = true
let isGoingLeft = false
let isGoingRight = false
let leftTimerId
let rightTimerId
let score = 0
let context
let musicIsPlaying = false
let copyRightMessage = " DoodleJump version by Santiago Hernandez \n all rights reserved \n Copyright © "
const NO_OF_HIGH_SCORES = 10;
const HIGH_SCORES = 'highScores';
function createDoodler() {
grid.appendChild(doodler)
doodler.classList.add('doodler')
doodlerLeftSpace = platforms[0].left
doodler.style.left = doodlerLeftSpace + 'px'
doodler.style.bottom = doodlerBottomSpace + 'px'
}
function control(e) {
if (e.key === "ArrowLeft") {
moveLeft()
} else if (e.key === "ArrowRight") {
moveRight()
} else if (e.key === "ArrowUp") {
moveStraight()
}
}
class Platform {
constructor(newPlatBottom) {
this.bottom = newPlatBottom
this.left = Math.random() * 315
this.visual = document.createElement('div')
const visual = this.visual
visual.classList.add('platform')
visual.style.left = this.left + 'px'
visual.style.bottom = this.bottom + 'px'
grid.appendChild(visual)
}
}
function createPlatforms() {
for (let i = 0; i < platformCount; i++) {
let platGap = 600 / platformCount
let newPlatBottom = 100 + i * platGap
let newPlatform = new Platform(newPlatBottom)
platforms.push(newPlatform)
console.log(platforms)
}
}
function movePlatforms() {
if (doodlerBottomSpace > 200) {
platforms.forEach(platform => {
platform.bottom -= 4
let visual = platform.visual
visual.style.bottom = platform.bottom + 'px'
if (platform.bottom < 10) {
let firstPlatform = platforms[0].visual
firstPlatform.classList.remove('platform')
platforms.shift()
score++
console.log(score)
console.log(platforms)
let newPlatform = new Platform(600)
platforms.push(newPlatform)
}
})
}
}
function jump() {
clearInterval(downTimerId)
isJumping = true
upTimerId = setInterval(function() {
doodlerBottomSpace += 20
doodler.style.bottom = doodlerBottomSpace + 'px'
if (doodlerBottomSpace > startPoint + 200) {
fall()
}
}, 30)
}
function fall() {
clearInterval(upTimerId)
isJumping = false
downTimerId = setInterval(function() {
doodlerBottomSpace -= 5
doodler.style.bottom = doodlerBottomSpace + 'px'
if (doodlerBottomSpace <= 0) {
gameOver()
}
platforms.forEach(platform => {
if ((doodlerBottomSpace >= platform.bottom) &&
(doodlerBottomSpace <= platform.bottom + 15) &&
((doodlerLeftSpace + 60) >= platform.left) &&
(doodlerLeftSpace <= (platform.left + 85)) &&
!isJumping
) {
console.log('landed')
startPoint = doodlerBottomSpace
jump()
}
})
}, 30)
}
function moveLeft() {
if (isGoingRight) {
clearInterval(rightTimerId)
isGoingRight = false
}
isGoingLeft = true
leftTimerId = setInterval(function() {
if (doodlerLeftSpace >= 0) {
doodlerLeftSpace -= 5
doodler.style.left = doodlerLeftSpace + 'px'
} else moveRight()
}, 30)
}
function moveRight() {
if (isGoingLeft) {
clearInterval(leftTimerId)
isGoingLeft = false
}
isGoingRight = true
rightTimerId = setInterval(function() {
if (doodlerLeftSpace <= 340) {
doodlerLeftSpace += 5
doodler.style.left = doodlerLeftSpace + 'px'
} else moveLeft()
}, 30)
}
function moveStraight() {
isGoingRight = false
isGoingLeft = false
clearInterval(rightTimerId)
clearInterval(leftTimerId)
}
function gameOver() {
console.log('GAME OVER')
isGameOver = true
try {
context.pause()
while (grid.firstChild) {
grid.removeChild(grid.firstChild)
}
grid.innerHTML = score
clearInterval(upTimerId)
clearInterval(downTimerId)
clearInterval(leftTimerId)
clearInterval(rightTimerId)
} catch (err) {
console.log('there was an error at gameover')
while (grid.firstChild) {
grid.removeChild(grid.firstChild)
}
grid.innerHTML = score
clearInterval(upTimerId)
clearInterval(downTimerId)
clearInterval(leftTimerId)
clearInterval(rightTimerId)
}
checkHighScore()
}
function saveHighScore(score, highScores) {
const name = prompt('You got a highscore! Enter name:');
const newScore = {
score,
name
};
// 1. Add to list
highScores.push(newScore);
// 2. Sort the list
highScores.sort((a, b) => b.score - a.score);
// 3. Select new list
highScores.splice(NO_OF_HIGH_SCORES);
// 4. Save to local storage
localStorage.setItem(HIGH_SCORES, JSON.stringify(highScores));
};
function checkHighScore() {
const highScores = JSON.parse(localStorage.getItem(HIGH_SCORES)) ? ? [];
const lowestScore = highScores[NO_OF_HIGH_SCORES - 1] ? .score ? ? 0;
if (score > lowestScore) {
saveHighScore(score, highScores); // TODO
showHighScores(); // TODO
}
}
function showHighScores() {
const highScores = JSON.parse(localStorage.getItem(HIGH_SCORES)) ? ? [];
const highScoreList = document.getElementById('highScores');
highScoreList.innerHTML = highScores.map((score) =>
`<li>${score.score} - ${score.name}</li>`
);
}
function start() {
if (!isGameOver) {
createPlatforms()
createDoodler()
setInterval(movePlatforms, 30)
jump()
document.addEventListener('keyup', control)
}
}
document.addEventListener('keypressed', control)
//attach to buttom
start()
//event listener to play music
document.addEventListener('keypress', function(e) {
if (e.keyCode == 32 || e.code == "Space") {
musicIsPlaying = true
context = new Audio("Music_level1.wav");
context.play()
context.loop = true
}
}) //end of event listener
})
.grid {
width: 400px;
height: 600px;
background-color: yellow;
position: relative;
font-size: 200px;
text-align: center;
background-image: url(bluesky_level1.gif);
background-size: contain;
background-repeat: no-repeat;
background-size: 400px 600px;
margin-right: auto;
margin-left: auto;
}
.doodler {
width: 60px;
height: 85px;
position: absolute;
background-image: url(mariobros_level1.png);
background-size: contain;
background-repeat: no-repeat;
background-size: 60px 85px;
filter: brightness(1.1);
mix-blend-mode: multiply;
}
#audio {
display: none
}
.platform {
width: 85px;
height: 15px;
position: absolute;
background-image: url(platform_tramp_level1.png);
background-size: contain;
background-repeat: no-repeat;
background-size: 85px 15px;
}
.volumeIcon {
width: 30px;
height: 30px;
position: absolute;
top: 570px;
background-image: url(volumeIconMuted.png);
background-size: contain;
background-repeat: no-repeat;
background-size: 30px 30px;
}
.unmutedIcon {
width: 30px;
height: 30px;
position: absolute;
top: 570px;
background-image: url(VolumeIcon.png);
background-size: contain;
background-repeat: no-repeat;
background-size: 30px 30px;
}
#highScores {
width: 400px;
height: 300px;
font-size: 30px;
font-family: "Georgia", "Times New Roman";
text-align: center;
position: absolute;
}
<ol id="highScores"></ol>
<div class="grid">
<div class="volumeIcon"></div>
</div>
I included what I think will reproduce the situation. Hope this helps you help me.
Why not just put:
<ol id = "highScores"></ol>
within a <div> and give that a class that has display: none initally? Like this:
<div class="high-scores-container">
<ol id = "highScores"></ol>
</div>
.high-scores-container {
display: none;
height: 100%;
}
Then when you run your showHighScores() function, grab the high-scores-container div and change the display to block and then at the same time, grab the grid div and set that to display: none. That will give you the effect of displaying your high scores on a separate page but you're just doing so with JS/CSS.

How to Write a High Performance Seamless Scroll Plugin?

<pre>
<code>
<template>
<div class="seamless-group" ref="group">
<ul>
<li v-for="item in data" :key="item._id">
<img :src="item.pic" />
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
data: {
default: [],
type: Array
}
},
mounted() {
this.seamlessScroll({
pd: 40,
gridWidth: 556
});
},
methods: {
seamlessScroll(options = {}) {
let defaultConfig = {
pd: 20,
gridWidth: null,
xCache: 0
};
options = Object.assign(defaultConfig,options);
let el = this.$refs.group.querySelector("ul");
let parentContainerWidth = 0;
let imgContainers = el.querySelectorAll("li");
let count = imgContainers.length;
if(!options.gridWidth) {
for(let i = 0; i < count ; i++) {
parentContainerWidth += imgContainers[i].getBoundingClientRect().width;
el.insertAdjacentElement("beforeend",imgContainers[i].cloneNode(true));
}
}else {
for(let i = 0; i < count ; i++) {
parentContainerWidth += options.gridWidth;
el.insertAdjacentElement("beforeend",imgContainers[i].cloneNode(true));
}
}
parentContainerWidth = parentContainerWidth + count * options.pd;
console.log(parentContainerWidth);
options.xCache = el.querySelector("li:first-child").offsetLeft;
let fz_left = parentContainerWidth * -1;
scroll();
function scroll() {
if(options.xCache <= fz_left) {
options.xCache = 0;
}
options.xCache -= 1;
el.style.transform = "translateX(" + options.xCache.toString() + "px)";
window.requestAnimationFrame(scroll);
}
}
}
}
</script>
<style lang="less">
.seamless-group {
position: relative;
overflow: hidden;
ul {
list-style: none;
padding: 0;
display: flex;
li {
width: 556px;
margin-right: 40px;
}
}
}
</style>
</code>
This is a vue component code, I tried to encapsulate it, I used translate mobile parent container, do not know whether it is the appropriate method?there is a problem, such as the screen width is too large second scroll content can not be fully covered, how can I effectively solve the problem?(This is the code for a vue.js component).
This is my code:

Javascript prototype extend base-class unable to access base-class properties/methods from prototype class

I have created a javascript class TkpSlider being inspired from this w3schools page. (JSFiddle)
var TkpSlider = function (args) {
args= args|| {};
};
var mainSwiper = new TkpSlider();
I have extended this to add some swipe ability being inspired from this page so that I can the slider works on user swipe. (JSFiddle)
var TkpSwiper = function (args) {
TkpSlider.call(this, args);
};
TkpSwiper.prototype = Object.create(TkpSlider.prototype);
var mainSwiper = new TkpSwiper();
I separated the code so I don't get confused later from the base code and if any additional function. But with OOP I have to extend by creating a new name for it TkpSwiper, But I want to find another way to use the same name TkpSlider.
I saw this article and tried to use prototype to extend TkpSlider in this JSFiddle (Snippet below). The problem is I can't access the public methods of the base class from child class. The sample javascript in the blog I tested working though, I must be missing something with my code. Could anyone shed some insight? Thanks.
var TkpSlider = function (args) {
args= args|| {};
//private variable
var btnPrev = args.btnPrev || "tkp-slide-btn-prev";//class for previous button (default: "tkp-slide-btn-prev")
var btnNext = args.btnNext || "tkp-slide-btn-next";//class for next button (default: "tkp-slide-btn-next")
var itemClass = args.itemClass || "tkp-slide-item"; //each item container class to slide (default: "tkp-slide-item")
var isAutoSlide = args.isAutoSlide || false;//set auto slide (default: false)
var autoSlideTimer = args.autoSlideTimer || 2000;//set auto slide timer when isAutoSlide is true(default: 2000)
var hideBtnNav = args.hideBtnNav || false; //hide button navigation(default: false)
var isRandom = args.isRandom || false; //whether next or previous slide should be random index
var slideIndex = args.startIndex || 0; //start slide index number (zero based, index 0 = slide number 1, default :0)
var itemNodes = document.getElementsByClassName(itemClass);
var itemLength = itemNodes.length;
var autoSlideInterval;
//public variable
this.testParentVar = "parent var";
//init function
init();
function init() {
if (itemLength > 0) {
showSlide();
bindEvent();
isAutoSlide ? setAutoSlide() : "";
hideBtnNav ? setHideBtnNav() : "";
} else {
throw "Cannot find slider item class";
}
}
//private function
function showSlide() {
if (slideIndex >= itemLength) { slideIndex = 0; }
if (slideIndex < 0) { slideIndex = (itemLength - 1); }
for (var i = 0; i < itemLength; i++) {
itemNodes[i].style.display = "none";
}
itemNodes[slideIndex].style.display = "block";
}
function nextPrevSlide(n) {
slideIndex += n;
showSlide();
}
function bindEvent() {
var btnPrevNode = document.getElementsByClassName(btnPrev);
if (btnPrevNode.length > 0) {
btnPrevNode[0].addEventListener("click", function () {
if (isRandom) {
setRandomSlideIndex();
} else {
nextPrevSlide(-1);
}
if (isAutoSlide) {
clearInterval(autoSlideInterval);
setAutoSlide();
}
});
} else {
throw "Cannot find button previous navigation";
}
var btnNextNode = document.getElementsByClassName(btnNext);
if (btnNextNode.length > 0) {
btnNextNode[0].addEventListener("click", function () {
if (isRandom) {
setRandomSlideIndex();
} else {
nextPrevSlide(1);
}
if (isAutoSlide) {
clearInterval(autoSlideInterval);
setAutoSlide();
}
});
} else {
throw "Cannot find button next navigation";
}
}
function setAutoSlide() {
autoSlideInterval = setInterval(function () {
if (isRandom) {
setRandomSlideIndex();
} else {
nextPrevSlide(1);
}
}, autoSlideTimer);
}
function setHideBtnNav() {
document.getElementsByClassName(btnPrev)[0].style.display = "none";
document.getElementsByClassName(btnNext)[0].style.display = "none";
}
function setRandomSlideIndex() {
var randomIndex = Math.floor(Math.random() * itemLength);
if (randomIndex == slideIndex) {
setRandomSlideIndex();
} else {
slideIndex = randomIndex;
showSlide();
}
}
//public function
this.nextPrevSlide = function (n) {
nextPrevSlide(n);
//console.log("nextPrevSlide");
};
//getter setter
this.setItemClass = function (prm) {
itemClass = prm;
};
this.getItemClass = function () {
return itemClass;
};
};
//Adding touch swiper
TkpSlider.prototype = function () {
var self = this;
var touchStartCoords = { 'x': -1, 'y': -1 }, // X and Y coordinates on mousedown or touchstart events.
touchEndCoords = { 'x': -1, 'y': -1 },// X and Y coordinates on mouseup or touchend events.
direction = 'undefined',// Swipe direction
minDistanceXAxis = 30,// Min distance on mousemove or touchmove on the X axis
maxDistanceYAxis = 30,// Max distance on mousemove or touchmove on the Y axis
maxAllowedTime = 1000,// Max allowed time between swipeStart and swipeEnd
startTime = 0,// Time on swipeStart
elapsedTime = 0,// Elapsed time between swipeStart and swipeEnd
//targetElement = document.getElementById('el'), // Element to delegate
targetElements = self.getItemClass()
;
init();
function init() {
addMultipleListeners(targetElements, 'mousedown touchstart', swipeStart);
addMultipleListeners(targetElements, 'mousemove touchmove', swipeMove);
addMultipleListeners(targetElements, 'mouseup touchend', swipeEnd);
}
function swipeStart(e) {
e = e ? e : window.event;
e = ('changedTouches' in e) ? e.changedTouches[0] : e;
touchStartCoords = { 'x': e.pageX, 'y': e.pageY };
startTime = new Date().getTime();
//targetElement.textContent = " ";
}
function swipeMove(e) {
e = e ? e : window.event;
e.preventDefault();
}
function swipeEnd(e) {
e = e ? e : window.event;
e = ('changedTouches' in e) ? e.changedTouches[0] : e;
touchEndCoords = { 'x': e.pageX - touchStartCoords.x, 'y': e.pageY - touchStartCoords.y };
elapsedTime = new Date().getTime() - startTime;
if (elapsedTime <= maxAllowedTime) {
if (Math.abs(touchEndCoords.x) >= minDistanceXAxis && Math.abs(touchEndCoords.y) <= maxDistanceYAxis) {
direction = (touchEndCoords.x < 0) ? 'left' : 'right';
switch (direction) {
case 'left':
//targetElement.textContent = "Left swipe detected";
console.log("swipe left");
self.nextPrevSlide(1);
break;
case 'right':
// targetElement.textContent = "Right swipe detected";
console.log("swipe right");
self.nextPrevSlide(-1);
break;
}
}
}
}
function addMultipleListeners(el, s, fn) {
var evts = s.split(' ');
var elLength = el.length;
for (var i = 0, iLen = evts.length; i < iLen; i++) {
if (elLength > 1) {
for (var j = 0; j < elLength; j++) {
el[j].addEventListener(evts[i], fn, false);
}
} else {
el.addEventListener(evts[i], fn, false);
}
}
}
}();
var mainSwiper = new TkpSlider();
.tkp-slide {
width: 100%;
position: relative; }
.tkp-slide .tkp-slide-item:not(:first-child) {
display: none; }
.tkp-slide .tkp-slide-item img {
width: 100%; }
.tkp-slide .tkp-slide-btn {
color: #fff !important;
background-color: #000 !important;
border: none;
display: inline-block;
outline: 0;
padding: 8px 16px;
vertical-align: middle;
overflow: hidden;
text-decoration: none;
text-align: center;
cursor: pointer;
white-space: nowrap;
opacity: 0.7; }
.tkp-slide .tkp-slide-btn:hover {
background-color: #333 !important; }
.tkp-slide .tkp-slide-btn-prev {
position: absolute;
top: 50%;
left: 0%;
transform: translate(0%, -50%);
-webkit-transform: translate(0%, -50%);
-ms-transform: translate(0%, -50%); }
.tkp-slide .tkp-slide-btn-next {
position: absolute;
top: 50%;
right: 0%;
transform: translate(0%, -50%);
-ms-transform: translate(0%, -50%); }
<section class="main-slide">
<div class="tkp-slide tkp-slide--container">
<div class="tkp-slide-item">
<a href="javascript:void(0)">
<img src="https://dummyimage.com/400x200/f2d9f2/080708&text=1" alt="Slide 1" />
</a>
</div>
<div class="tkp-slide-item">
<a href="javascript:void(0)">
<img src="https://dummyimage.com/400x200/f2d9f2/080708&text=2" alt="Slide 2" />
</a>
</div>
<div class="tkp-slide-item">
<a href="javascript:void(0)">
<img src="https://dummyimage.com/400x200/f2d9f2/080708&text=3" alt="Slide 3" />
</a>
</div>
<div class="tkp-slide-item">
<a href="javascript:void(0)">
<img src="https://dummyimage.com/400x200/f2d9f2/080708&text=4" alt="Slide 4" />
</a>
</div>
<div class="tkp-slide-item">
<a href="javascript:void(0)">
<img src="https://dummyimage.com/400x200/f2d9f2/080708&text=5" alt="Slide 5" />
</a>
</div>
<ul>
<li class="tkp-slide-btn tkp-slide-btn-prev">
❮
</li>
<li class="tkp-slide-btn tkp-slide-btn-next">
❯
</li>
</ul>
</div>
</section>
Problem 1: You are not returning anything from the prototype function. You have to return each public method as properties of the return Object.
return {
initSwiper : init,
method1 : method1,
method2 : method2,
};
Problem 2: You need to set variables inside the init function. Outside it this variable won't refer to TkpSlider.
// init();
function init() {
self=this;
var targetElements = document.getElementsByClassName(this.getItemClass());
addMultipleListeners(targetElements, 'mousedown touchstart', swipeStart);
addMultipleListeners(targetElements, 'mousemove touchmove', swipeMove);
addMultipleListeners(targetElements, 'mouseup touchend', swipeEnd);
return this;
}
Problem 3 You need to manually call the initSwiper() method to get your variables initialized when you create the TkpSlider Object.
var mainSwiper = new TkpSlider().initSwiper();
Then it will work as you need and you can call public methods if you need.
mainSwiper.method1();
mainSwiper.method2();
Working Snippet:
var TkpSlider = function(args) {
args = args || {};
//private variable
var btnPrev = args.btnPrev || "tkp-slide-btn-prev"; //class for previous button (default: "tkp-slide-btn-prev")
var btnNext = args.btnNext || "tkp-slide-btn-next"; //class for next button (default: "tkp-slide-btn-next")
var itemClass = args.itemClass || "tkp-slide-item"; //each item container class to slide (default: "tkp-slide-item")
var isAutoSlide = args.isAutoSlide || false; //set auto slide (default: false)
var autoSlideTimer = args.autoSlideTimer || 2000; //set auto slide timer when isAutoSlide is true(default: 2000)
var hideBtnNav = args.hideBtnNav || false; //hide button navigation(default: false)
var isRandom = args.isRandom || false; //whether next or previous slide should be random index
var slideIndex = args.startIndex || 0; //start slide index number (zero based, index 0 = slide number 1, default :0)
var itemNodes = document.getElementsByClassName(itemClass);
var itemLength = itemNodes.length;
var autoSlideInterval;
//public variable
this.testParentVar = "parent var";
//init function
init();
function init() {
if (itemLength > 0) {
showSlide();
bindEvent();
isAutoSlide ? setAutoSlide() : "";
hideBtnNav ? setHideBtnNav() : "";
} else {
throw "Cannot find slider item class";
}
}
//private function
function showSlide() {
if (slideIndex >= itemLength) {
slideIndex = 0;
}
if (slideIndex < 0) {
slideIndex = (itemLength - 1);
}
for (var i = 0; i < itemLength; i++) {
itemNodes[i].style.display = "none";
}
itemNodes[slideIndex].style.display = "block";
}
function nextPrevSlide(n) {
slideIndex += n;
showSlide();
}
function bindEvent() {
var btnPrevNode = document.getElementsByClassName(btnPrev);
if (btnPrevNode.length > 0) {
btnPrevNode[0].addEventListener("click", function() {
if (isRandom) {
setRandomSlideIndex();
} else {
nextPrevSlide(-1);
}
if (isAutoSlide) {
clearInterval(autoSlideInterval);
setAutoSlide();
}
});
} else {
throw "Cannot find button previous navigation";
}
var btnNextNode = document.getElementsByClassName(btnNext);
if (btnNextNode.length > 0) {
btnNextNode[0].addEventListener("click", function() {
if (isRandom) {
setRandomSlideIndex();
} else {
nextPrevSlide(1);
}
if (isAutoSlide) {
clearInterval(autoSlideInterval);
setAutoSlide();
}
});
} else {
throw "Cannot find button next navigation";
}
}
function setAutoSlide() {
autoSlideInterval = setInterval(function() {
if (isRandom) {
setRandomSlideIndex();
} else {
nextPrevSlide(1);
}
}, autoSlideTimer);
}
function setHideBtnNav() {
document.getElementsByClassName(btnPrev)[0].style.display = "none";
document.getElementsByClassName(btnNext)[0].style.display = "none";
}
function setRandomSlideIndex() {
var randomIndex = Math.floor(Math.random() * itemLength);
if (randomIndex == slideIndex) {
setRandomSlideIndex();
} else {
slideIndex = randomIndex;
showSlide();
}
}
//public function
this.nextPrevSlide = function(n) {
nextPrevSlide(n);
//console.log("nextPrevSlide");
};
//getter setter
this.setItemClass = function(prm) {
itemClass = prm;
};
this.getItemClass = function() {
return itemClass;
};
};
//Adding touch swiper
TkpSlider.prototype = function() {
var self;
var touchStartCoords = {
'x': -1,
'y': -1
}, // X and Y coordinates on mousedown or touchstart events.
touchEndCoords = {
'x': -1,
'y': -1
}, // X and Y coordinates on mouseup or touchend events.
direction = 'undefined', // Swipe direction
minDistanceXAxis = 30, // Min distance on mousemove or touchmove on the X axis
maxDistanceYAxis = 30, // Max distance on mousemove or touchmove on the Y axis
maxAllowedTime = 1000, // Max allowed time between swipeStart and swipeEnd
startTime = 0, // Time on swipeStart
elapsedTime = 0, // Elapsed time between swipeStart and swipeEnd
//targetElement = document.getElementById('el'), // Element to delegate
targetElements; //this.prototype.getItemClass()
;
// init();
function initSwiper() {
self = this;
var targetElements = document.getElementsByClassName(this.getItemClass());
addMultipleListeners(targetElements, 'mousedown touchstart', swipeStart);
addMultipleListeners(targetElements, 'mousemove touchmove', swipeMove);
addMultipleListeners(targetElements, 'mouseup touchend', swipeEnd);
return this;
}
function swipeStart(e) {
e.preventDefault();
e = e ? e : window.event;
e = ('changedTouches' in e) ? e.changedTouches[0] : e;
touchStartCoords = {
'x': e.pageX,
'y': e.pageY
};
startTime = new Date().getTime();
//targetElement.textContent = " ";
}
function swipeMove(e) {
e = e ? e : window.event;
e.preventDefault();
}
function swipeEnd(e) {
e.preventDefault();
e = e ? e : window.event;
e = ('changedTouches' in e) ? e.changedTouches[0] : e;
touchEndCoords = {
'x': e.pageX - touchStartCoords.x,
'y': e.pageY - touchStartCoords.y
};
elapsedTime = new Date().getTime() - startTime;
if (elapsedTime <= maxAllowedTime) {
if (Math.abs(touchEndCoords.x) >= minDistanceXAxis && Math.abs(touchEndCoords.y) <= maxDistanceYAxis) {
direction = (touchEndCoords.x < 0) ? 'left' : 'right';
switch (direction) {
case 'left':
//targetElement.textContent = "Left swipe detected";
console.log("swipe left");
self.nextPrevSlide(1);
break;
case 'right':
// targetElement.textContent = "Right swipe detected";
console.log("swipe right");
self.nextPrevSlide(-1);
break;
}
}
}
}
function addMultipleListeners(el, s, fn) {
var evts = s.split(' ');
var elLength = el.length;
for (var i = 0, iLen = evts.length; i < iLen; i++) {
if (elLength > 1) {
for (var j = 0; j < elLength; j++) {
el[j].addEventListener(evts[i], fn, false);
}
} else {
el.addEventListener(evts[i], fn, false);
}
}
}
return {
initSwiper: initSwiper
};
}();
var mainSwiper = new TkpSlider().initSwiper();
.tkp-slide {
width: 100%;
position: relative;
}
.tkp-slide .tkp-slide-item:not(:first-child) {
display: none;
}
.tkp-slide .tkp-slide-item img {
width: 100%;
}
.tkp-slide .tkp-slide-btn {
color: #fff !important;
background-color: #000 !important;
border: none;
display: inline-block;
outline: 0;
padding: 8px 16px;
vertical-align: middle;
overflow: hidden;
text-decoration: none;
text-align: center;
cursor: pointer;
white-space: nowrap;
opacity: 0.7;
}
.tkp-slide .tkp-slide-btn:hover {
background-color: #333 !important;
}
.tkp-slide .tkp-slide-btn-prev {
position: absolute;
top: 50%;
left: 0%;
transform: translate(0%, -50%);
-webkit-transform: translate(0%, -50%);
-ms-transform: translate(0%, -50%);
}
.tkp-slide .tkp-slide-btn-next {
position: absolute;
top: 50%;
right: 0%;
transform: translate(0%, -50%);
-ms-transform: translate(0%, -50%);
}
<section class="main-slide">
<div class="tkp-slide tkp-slide--container">
<div class="tkp-slide-item">
<a href="javascript:void(0)">
<img src="https://dummyimage.com/400x200/f2d9f2/080708&text=1" alt="Slide 1" />
</a>
</div>
<div class="tkp-slide-item">
<a href="javascript:void(0)">
<img src="https://dummyimage.com/400x200/f2d9f2/080708&text=2" alt="Slide 2" />
</a>
</div>
<div class="tkp-slide-item">
<a href="javascript:void(0)">
<img src="https://dummyimage.com/400x200/f2d9f2/080708&text=3" alt="Slide 3" />
</a>
</div>
<div class="tkp-slide-item">
<a href="javascript:void(0)">
<img src="https://dummyimage.com/400x200/f2d9f2/080708&text=4" alt="Slide 4" />
</a>
</div>
<div class="tkp-slide-item">
<a href="javascript:void(0)">
<img src="https://dummyimage.com/400x200/f2d9f2/080708&text=5" alt="Slide 5" />
</a>
</div>
<ul>
<li class="tkp-slide-btn tkp-slide-btn-prev">
❮
</li>
<li class="tkp-slide-btn tkp-slide-btn-next">
❯
</li>
</ul>
</div>
</section>
Prototype confusion: which this is this?
TkpSlider.prototype = function() {
console.log(this); /* this this is Child */
init();
function init() {
console.log(this); /* this this is Child, called from child context */
}
function initSwiper() {
console.log(this); /* this this is Parent, called from parent context */
}
function swipeStartEventHandler(event) {
console.log(this); /* this this is HTML element #customId*/
}
return {
initSwiper: initSwiper,
};
}();
new TkpSlider().initSwiper();

Angular 2 tooltip should auto adjust position itself in mobile or when window is resized

I am looking for Angular 2 tooltip which should auto adjust position itself (from left and right at least) in mobile or when the window is resized so that it should be visible on screen completely. I am sharing my current code on Plunker, appreciate if anyone can help me on this. Thanks.
Tooltip Plunk
Tooltip Image
This position function I am using:
private positionElements(hostEl: HTMLElement, targetEl: HTMLElement, positionStr: string, appendToBody: boolean = false): { top: number, left: number } {
let positionStrParts = positionStr.split("-");
let pos0 = positionStrParts[0];
let pos1 = positionStrParts[1] || "center";
let hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
let targetElWidth = targetEl.offsetWidth;
let targetElHeight = targetEl.offsetHeight;
let shiftWidth: any = {
center: function (): number {
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
},
left: function (): number {
return hostElPos.left;
},
right: function (): number {
return hostElPos.left + hostElPos.width;
}
};
let shiftHeight: any = {
center: function (): number {
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
},
top: function (): number {
return hostElPos.top;
},
bottom: function (): number {
return hostElPos.top + hostElPos.height;
}
};
let targetElPos: { top: number, left: number };
switch (pos0) {
case "right":
targetElPos = {
top: shiftHeight[pos1](),
left: shiftWidth[pos0]()
};
break;
case "left":
targetElPos = {
top: shiftHeight[pos1](),
left: hostElPos.left - targetElWidth
};
break;
case "bottom":
targetElPos = {
top: shiftHeight[pos0](),
left: shiftWidth[pos1](this.useAlternateMobileTooltip)
};
break;
default:
targetElPos = {
top: hostElPos.top - targetElHeight,
left: shiftWidth[pos1]()
};
break;
}
return targetElPos;
}
private position(nativeEl: HTMLElement): { width: number, height: number, top: number, left: number } {
let offsetParentBCR = { top: 0, left: 0 };
const elBCR = this.offset(nativeEl);
const offsetParentEl = this.parentOffsetEl(nativeEl);
if (offsetParentEl !== window.document) {
offsetParentBCR = this.offset(offsetParentEl);
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
}
const boundingClientRect = nativeEl.getBoundingClientRect();
return {
width: boundingClientRect.width || nativeEl.offsetWidth,
height: boundingClientRect.height || nativeEl.offsetHeight,
top: elBCR.top - offsetParentBCR.top,
left: elBCR.left - offsetParentBCR.left
};
}
private offset(nativeEl: any): { width: number, height: number, top: number, left: number } {
const boundingClientRect = nativeEl.getBoundingClientRect();
return {
width: boundingClientRect.width || nativeEl.offsetWidth,
height: boundingClientRect.height || nativeEl.offsetHeight,
top: boundingClientRect.top + (window.pageYOffset || window.document.documentElement.scrollTop),
left: boundingClientRect.left + (window.pageXOffset || window.document.documentElement.scrollLeft)
};
}
private getStyle(nativeEl: HTMLElement, cssProp: string): string {
if ((nativeEl as any).currentStyle) // IE
return (nativeEl as any).currentStyle[cssProp];
if (window.getComputedStyle)
return (window.getComputedStyle(nativeEl) as any)[cssProp];
// finally try and get inline style
return (nativeEl.style as any)[cssProp];
}
private isStaticPositioned(nativeEl: HTMLElement): boolean {
return (this.getStyle(nativeEl, "position") || "static") === "static";
}
private parentOffsetEl(nativeEl: HTMLElement): any {
let offsetParent: any = nativeEl.offsetParent || window.document;
while (offsetParent && offsetParent !== window.document && this.isStaticPositioned(offsetParent)) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || window.document;
}
Try this:
Tooltip location from left = hostElPos.left
Tooltip location from right = current window width - hostElPos.left - hostElPos.width;
Now,
First tooltip = closest from left edge
Last tooltip = closest from the right edge
Use this information with the center function to apply style on the first/last tooltip. Use media query for mobile.
HTML:
[class.alternate-left]="useLeft"
[class.alternate-right]=“useRight"
Component:
useLeft = false;
useRight = false;
let useWindowWidth = window.screen.width;
let targetElemetOnLeft = hostElPos.left;
let targetElemetOnRight = (useWindowWidth - hostElPos.left) - hostElPos.width;
this.useLeft = (hostElPos.width < 90 && targetElemetOnLeft < 30);
this.useRight = (hostElPos.width < 90 && targetElemetOnRight < 50);
center: function (isLeft: boolean, isRight: boolean): number {
if (isLeft) {
return hostElPos.left;
}
else if (isRight) {
return hostElPos.left - targetElWidth;
}
else{
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
}
Working Plunk

Why isn't it possible to change max-height with % in javascript?

I'm trying to build a responsive menu, with a hamburger icon. I want the menu list to slide in and out, no jquery - pure javascript only.
HTML :
<div id="animation">
</div>
<button id="toggle">Toggle</button>
CSS :
div {
width: 300px;
height: 300px;
background-color: blue;
}
Javascript :
var but = document.getElementById('toggle');
var div = document.getElementById('animation');
var animate = function(type, callback){
var inter = -1, start = 100, end = 0;
if(type==true){
inter = 1;
start = 0;
end = 100;
}
var si = setInterval(function(){
console.log('maxheight');
div.style.maxHeight = (start + inter) + '%';
if(start == end){
clearInterval(si);
}
}, 10);
}
var hidden = false;
but.onclick = function(){
animate(hidden, function(){
hidden = (hidden == false) ? true : false;
});
}
div.style.maxHeight = "50%";
The problem is that proportional height in an element needs a fixed height on the parent, and you didn't provided any parent with a fixed height because for the maxHeight property too the % Defines the maximum height in % of the parent element.
You have to put your div in a parent container with a fixed height, this is your working code:
var but = document.getElementById('toggle');
var div = document.getElementById('animation');
var animate = function(type, callback) {
var inter = -1,
start = 100,
end = 0;
if (type) {
inter = 1;
start = 0;
end = 100;
}
var si = setInterval(function() {
console.log('maxheight');
div.style.maxHeight = (start + inter) + '%';
if (start == end) {
clearInterval(si);
}
}, 10);
}
var hidden = false;
but.onclick = function() {
animate(hidden, function() {
hidden = !hidden ;
});
}
div.style.maxHeight = "50%";
#animation {
width: 300px;
height: 300px;
background-color: blue;
}
#parent {
width: 500px;
height: 500px;
}
<div id="parent">
<div id="animation">
</div>
<button id="toggle">Toggle</button>
</div>
Note:
As stated in comments there are some statements in your JavaScript code that need to be adjusted:
if(type==true) can be written as if(type).
hidden = (hidden == false) ? true : false; can be shortened to hidden = !hidden
There seems to be a few errors with your code. I have fixed the js and added comments to what I have changed
var but = document.getElementById('toggle');
var div = document.getElementById('animation');
var animate = function (type, callback) {
var start = 100,
end = 0;
if (type) {
start = 0;
end = 100;
}
var si = setInterval(function () {
if (type) { // check whether to open or close animation
start++;
} else {
start--
}
div.style.maxHeight = start + '%';
if (start == end) {
clearInterval(si);
}
}, 10);
callback.call(this); // do the callback function
}
var hidden = false;
but.onclick = function () {
animate(hidden, function () {
hidden = !hidden; // set hidden to opposite
});
}
/*make sure parent container has a height set or max height won't work*/
html, body {
height:100%;
margin:0;
padding:0;
}
div {
width: 300px;
height: 300px;
background-color: blue;
}
<div id="animation"></div>
<button id="toggle">Toggle</button>
Example Fiddle

Categories