Double scrollbar with shrinking toolbar - css

I am working on a photo gallery in AngularJS using Angular Material (run the snippet in fullscreen to see my problem).
var app = angular.module('app', ['ngMaterial']);
app.controller('TitleController', function($scope) {
$scope.title = 'Gallery';
});
app.controller('GalleryCtrl', function($scope, $http, $q, $mdMedia, $mdDialog) {
//https://material.angularjs.org/latest/demo/virtualRepeat
$scope.Images = [],
//add more images
$scope.LoadMore = function() {
for (i = 0; i < 25; i++) {
var randomWidth = Math.round(Math.random() * (800 - 400) + 400);
var randomHeight = Math.round(Math.random() * (800 - 400) + 400);
$scope.Images.push({
src: "http://placehold.it/" + randomWidth + "x" + randomHeight + "/",
id: Math.round(Math.random() * 10000)
});
};
}
$scope.ShowDetails = function(ev, number) {
var useFullScreen = ($mdMedia('sm') || $mdMedia('xs')) && $scope.customFullscreen;
$mdDialog.show({
controller: DialogController,
templateUrl: 'Home/GetInfo?id=' + number,
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose: true,
fullscreen: useFullScreen
})
$scope.$watch(function() {
return $mdMedia('xs') || $mdMedia('sm');
}, function(wantsFullScreen) {
$scope.customFullscreen = (wantsFullScreen === true);
});
};
function DialogController($scope, $mdDialog) {
$scope.hide = function() {
$mdDialog.hide();
};
$scope.cancel = function() {
$mdDialog.cancel();
};
$scope.answer = function(answer) {
$mdDialog.hide(answer);
};
}
//initial loading
$scope.LoadMore();
});
body {
background: #eeeeee;
}
html {
background: #eeeeee;
}
.gridListdemoBasicUsage md-grid-list {
margin: 8px;
}
.gridListdemoBasicUsage .green {
background: #b9f6ca;
}
.gridListdemoBasicUsage md-grid-tile {
transition: all 400ms ease-out 50ms;
}
.responsiveImage {
max-width: 100%;
max-height: 100%;
}
md-content {
background: #eeeeee;
position: relative;
}
.fit {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.toolbardemoScrollShrink .face {
width: 48px;
margin: 16px;
border-radius: 48px;
border: 1px solid #ddd;
}
.md-toolbar-tools {
background-color: #259b24;
}
.dialogdemoBasicUsage #popupContainer {
position: relative;
}
.dialogdemoBasicUsage .footer {
width: 100%;
text-align: center;
margin-left: 20px;
}
.dialogdemoBasicUsage .footer,
.dialogdemoBasicUsage .footer > code {
font-size: 0.8em;
margin-top: 50px;
}
.dialogdemoBasicUsage button {
width: 200px;
}
.dialogdemoBasicUsage div#status {
color: #c60008;
}
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.7/angular-material.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular-animate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular-aria.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.7/angular-material.min.js"></script>
<body ng-app="app" ng-controller="GalleryCtrl as gc" ng-cloak="" id="popupContainer" class="gridListdemoBasicUsage dialogdemoBasicUsage">
<md-toolbar md-scroll-shrink="" ng-if="true" ng-controller="TitleController">
<div class="md-toolbar-tools">
<h3><span>{{title}}</span></h3>
</div>
</md-toolbar>
<md-content style="height:100vh" />
<md-grid-list md-cols-xs="1" md-cols-sm="2" md-cols-md="4" md-cols-gt-md="6" md-row-height-gt-md="1:1" md-row-height="2:2" md-gutter="12px" md-gutter-gt-sm="8px">
<md-grid-tile ng-click="ShowDetails($event, n.id)" ng-repeat="n in Images" class="green">
<img class="responsiveImage" src="{{n.src}}">
<md-grid-tile-footer>
<h3>Photo number {{n.id}}</h3>
</md-grid-tile-footer>
</md-grid-tile>
</md-grid-list>
<section layout="row" layout-sm="column" layout-align="center center" layout-wrap="">
<md-button class="md-raised md-primary" ng-click="LoadMore()">Primary</md-button>
</section>
</body>
Go fullscreen, scroll to the bottom of the page, and press a button to load more images. The problem I'm having: I am trying to get the toolbar at the top of the screen to disappear when scrolling down, and appear again when scrolling up. However, 2 scrollbars appear, and only the right one affects the toolbar.The left scrollbar actually scrolls all the way down on the page.
My desired situation: only 1 visible scrollbar to scroll down the entire gallery, that also makes the toolbar appear and disappear. How do I do this?

try adding overflow-y: hidden; to the body css rule.

Related

CSS mix blend mode / only white or black

Hello I'm currently trying to find a solution to make a sticky text black if the background is white or black in any other case. During my research I found mix blend mode property but it seems very complex to make what I want.
.sticky {
position: -webkit-sticky;
position: sticky;
top: 20px;
color:white;
font-size:60px;
mix-blend-mode: difference;
}
.panel {
height: 80vh;
widht: 100%;
}
.bg-black {
background: black;
}
.bg-red {
background: red;
}
.bg-blue {
background: blue;
}
.bg-green {
background: green;
}
<div class="sticky">
My text
</div>
<div>
<section class="panel"></section>
<section class="panel bg-black"></section>
<section class="panel bg-red"></section>
<section class="panel bg-blue"></section>
<section class="panel bg-green"></section>
</div>
Does someone know a hack or a package that can help me?
Thanks a lot
It is possible to make it with CSS only, by not applying blend mode to the sticky elements, but to the background(::before, ::after) items.
.bg-field::before, .bg-field::after {
background-color: white;
mix-blend-mode: difference;
pointer-events: none;
content: "";
position: absolute;
bottom: 0;
top: 0;
right: 0;
left: 0;
}
.bg-field::before {
z-index: 1;
}
.bg-field::after {
background-color: red;
z-index: -1;
}
Everything inside the bg field (or even outside if its a fixed element) will be colored
I made a code snippet displaying how it works: https://codepen.io/AndrewKnife/pen/XWzBpeN
I'm not sure it's possible with mix-blend-mode, can do what you want with filter and background-clip: text though:
// can ignore this, it's just making the sliders work as R G B
function updateColor() {
const r = document.getElementById('r').value;
const g = document.getElementById('g').value;
const b = document.getElementById('b').value;
document.querySelector('.container').style.background = `rgb(${r},${g},${b}`;
}
.container {
background: white;
}
.contrast-text {
font-size: 50vmin;
background: inherit;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
filter:
sepia(5) /* add some color to grey so the rest works */
saturate(100) /* increase strength of color channels */
invert(1) /* invert the color */
grayscale(1) /* make it grey */
contrast(9); /* make it black/white */
}
<input type="range" onchange="updateColor()" min=0 max=255 value=255 id="r">
<input type="range" onchange="updateColor()" min=0 max=255 value=255 id="g">
<input type="range" onchange="updateColor()" min=0 max=255 value=255 id="b">
<div class="container">
<div class="contrast-text"> Text </div>
</div>
I finally find something great ! It's not as beautiful as mix-blend-mode but it do the job.
I'd prefer to stay 100% css because it require use of ScrollMagic
const controller = new ScrollMagic.Controller();
const sections = document.querySelectorAll('section');
const menu = document.querySelector('.my-text');
sections.forEach((section, index, arr) => {
const trigger = '#' + section.id;
const backgroundColor = window.getComputedStyle(section, null).getPropertyValue('background-color');
const textColor = getContrastYIQ(backgroundColor);
let previousBackgroundColor = backgroundColor;
let previousTextColor = getContrastYIQ(previousBackgroundColor);
if (index >= 1) {
previousBackgroundColor = window.getComputedStyle(arr[index - 1], null).getPropertyValue('background-color');
previousTextColor = getContrastYIQ(previousBackgroundColor);
}
new ScrollMagic.Scene({
triggerElement: trigger,
triggerHook: "onLeave",
offset: -50,
reverse: true
})
.on("enter", function() {
menu.classList.remove(previousTextColor);
menu.classList.add(textColor);
})
.on("leave", function() {
menu.classList.remove(textColor); menu.classList.add(previousTextColor);
})
.addTo(controller);
})
// Color contrast helper function
// https://en.wikipedia.org/wiki/YIQ
function getContrastYIQ(rgb) {
rgb = rgb.substring(4, rgb.length - 1)
.replace(/ /g, '')
.split(',');
const yiq = ((rgb[0] * 299) + (rgb[1] * 587) + (rgb[2] * 114)) / 1000;
return (yiq >= 128) ? 'black' : 'white';
}
section {
min-height: 80vh;
}
.my-text {
position: sticky;
top: 5vh;
color: white;
}
.black {
color: black;
&:before {
background: black;
box-shadow: 0 0.4em 0 0 black, 0 0.80em 0 0 black;
}
}
#s1 {
background-color: black;
}
#s2 {
background-color: white;
}
#s3 {
background-color: #111;
}
#s4 {
background-color: #9f3;
}
#s5 {
background-color: #145;
}
#s6 {
background-color: #f5f;
}
<script ></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.5/ScrollMagic.min.js"></script>
<div class="my-text">
MY TEXT</div>
<section id="s1">
</section>
<section id="s2"></section>
<section id="s3"></section>
<section id="s4"></section>
<section id="s5"></section>
<section id="s6"></section>

section content scaling with background

I am trying to stop my section content from scaling with my section background. the scale of the background is perfect but it seems to make the content scale also, I am using gsap library I have tried creating a container inside the section and give it absolute but nothing I do stops the content inside from scaling
//First the variables our app is going to use need to be declared
//References to DOM elements
var $window = $(window);
var $document = $(document);
//Only links that starts with #
var $navButtons = $("nav a").filter("[href^=#]");
var $navGoPrev = $(".go-prev");
var $navGoNext = $(".go-next");
var $sectionsContainer = $(".sections-container");
var $sections = $(".section");
var $currentSection = $sections.first();
//Animating flag - is our app animating
var isAnimating = false;
//The height of the window
var pageHeight = $window.innerHeight();
//Key codes for up and down arrows on keyboard. We'll be using this to navigate change sections using the keyboard
var keyCodes = {
UP : 38,
DOWN: 40
}
//Going to the first section
goToSection($currentSection);
/*
* Adding event listeners
* */
$window.on("resize", onResize).resize();
$window.on("mousewheel DOMMouseScroll", onMouseWheel);
$document.on("keydown", onKeyDown);
$navButtons.on("click", onNavButtonClick);
$navGoPrev.on("click", goToPrevSection);
$navGoNext.on("click", goToNextSection);
/*
* Internal functions
* */
/*
* When a button is clicked - first get the button href, and then section to the container, if there's such a container
* */
function onNavButtonClick(event)
{
//The clicked button
var $button = $(this);
//The section the button points to
var $section = $($button.attr("href"));
//If the section exists, we go to it
if($section.length)
{
goToSection($section);
event.preventDefault();
}
}
/*
* Getting the pressed key. Only if it's up or down arrow, we go to prev or next section and prevent default behaviour
* This way, if there's text input, the user is still able to fill it
* */
function onKeyDown(event)
{
var PRESSED_KEY = event.keyCode;
if(PRESSED_KEY == keyCodes.UP)
{
goToPrevSection();
event.preventDefault();
}
else if(PRESSED_KEY == keyCodes.DOWN)
{
goToNextSection();
event.preventDefault();
}
}
/*
* When user scrolls with the mouse, we have to change sections
* */
function onMouseWheel(event)
{
//Normalize event wheel delta
var delta = event.originalEvent.wheelDelta / 30 || -event.originalEvent.detail;
//If the user scrolled up, it goes to previous section, otherwise - to next section
if(delta < -1)
{
goToNextSection();
}
else if(delta > 1)
{
goToPrevSection();
}
event.preventDefault();
}
/*
* If there's a previous section, section to it
* */
function goToPrevSection()
{
if($currentSection.prev().length)
{
goToSection($currentSection.prev());
}
}
/*
* If there's a next section, section to it
* */
function goToNextSection()
{
if($currentSection.next().length)
{
goToSection($currentSection.next());
}
}
/*
* Actual transition between sections
* */
function goToSection($section)
{
//If the sections are not changing and there's such a section
if(!isAnimating && $section.length)
{
//setting animating flag to true
isAnimating = true;
//Sliding to current section
TweenLite.set($currentSection, {autoAlpha: 0, display: 'none'});
$currentSection = $section;
TweenLite.set($currentSection, {display: 'block'});
TweenLite.fromTo($currentSection, 0.6, {scale: 0.9, autoAlpha: 0}, {scale: 1, autoAlpha: 1, ease: Power1.easeOut, onComplete: onSectionChangeEnd, onCompleteScope: this});
//Animating menu items
TweenLite.to($navButtons.filter(".active"), 0.5, {className: "-=active"});
TweenLite.to($navButtons.filter("[href=#" + $currentSection.attr("id") + "]"), 0.5, {className: "+=active"});
}
}
/*
* Once the sliding is finished, we need to restore "isAnimating" flag.
* You can also do other things in this function, such as changing page title
* */
function onSectionChangeEnd()
{
isAnimating = false;
}
/*
* When user resize it's browser we need to know the new height, so we can properly align the current section
* */
function onResize(event)
{
//This will give us the new height of the window
var newPageHeight = $window.innerHeight();
/*
* If the new height is different from the old height ( the browser is resized vertically ), the sections are resized
* */
if(pageHeight !== newPageHeight)
{
pageHeight = newPageHeight;
//This can be done via CSS only, but fails into some old browsers, so I prefer to set height via JS
TweenLite.set([$sectionsContainer, $sections], {height: pageHeight + "px"});
//The current section should be always on the top
TweenLite.set($sectionsContainer, {scrollTo: {y: pageHeight * $currentSection.index() }});
}
}
body, div, p {
margin: 0;
padding: 0;
}
body {
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
font-weight: 300;
letter-spacing: 0.0625em;
background-color: #000;
}
h1{
color: #fff;
}
.sections-container {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 10;
}
.section {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
display: none;
visibility: hidden;
opacity: 0;
}
#section-1 {
display: block;
visibility: visible;
opacity: 1;
}
.section .centered h1 {
text-align: center;
}
.section .centered p {
text-align: center;
}
#section-1 {
background-color: #5A4748;
}
#section-2 {
background-color: #45959b;
}
#section-3 {
background-color: #778899;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenLite.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/plugins/CSSPlugin.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/plugins/ScrollToPlugin.min.js"></script>
<div class="sections-container">
<div class="section" id="section-1">
<div class="centered">
<h1>1</h1>
</div>
</div>
<div class="section" id="section-2">
<div class="centered">
<h1>2</h1>
</div>
</div>
<div class="section" id="section-3">
<div class="centered">
<h1>3</h1>
</div>
</div>
</div>
Would this be what you're looking for? I basically added a background div
<div id="background"></div>
#background {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
display: block;
visibility: visible;
opacity: 1;
}
which is assigned a background class based on the section which is currently visible.
var $background = $("#background");
...
$background.classList = "";
TweenLite.set($background, {
className: '+=bg-' + ($sections.index($currentSection) + 1)
});
TweenLite.fromTo($background, 0.6, {
scale: 0.9,
autoAlpha: 0
}, {
scale: 1,
autoAlpha: 1,
ease: Power1.easeOut,
onComplete: onSectionChangeEnd,
onCompleteScope: this
});
You can choose to remove the autoAlpha tween on the section numbers if you so prefer.
//First the variables our app is going to use need to be declared
//References to DOM elements
var $window = $(window);
var $document = $(document);
//Only links that starts with #
var $navButtons = $("nav a").filter("[href^=#]");
var $navGoPrev = $(".go-prev");
var $navGoNext = $(".go-next");
var $sectionsContainer = $(".sections-container");
var $sections = $(".section");
var $background = $("#background");
var $currentSection = $sections.first();
//Animating flag - is our app animating
var isAnimating = false;
//The height of the window
var pageHeight = $window.innerHeight();
//Key codes for up and down arrows on keyboard. We'll be using this to navigate change sections using the keyboard
var keyCodes = {
UP: 38,
DOWN: 40
}
//Going to the first section
goToSection($currentSection);
/*
* Adding event listeners
* */
$window.on("resize", onResize).resize();
$window.on("mousewheel DOMMouseScroll", onMouseWheel);
$document.on("keydown", onKeyDown);
$navButtons.on("click", onNavButtonClick);
$navGoPrev.on("click", goToPrevSection);
$navGoNext.on("click", goToNextSection);
/*
* Internal functions
* */
/*
* When a button is clicked - first get the button href, and then section to the container, if there's such a container
* */
function onNavButtonClick(event) {
//The clicked button
var $button = $(this);
//The section the button points to
var $section = $($button.attr("href"));
//If the section exists, we go to it
if ($section.length) {
goToSection($section);
event.preventDefault();
}
}
/*
* Getting the pressed key. Only if it's up or down arrow, we go to prev or next section and prevent default behaviour
* This way, if there's text input, the user is still able to fill it
* */
function onKeyDown(event) {
var PRESSED_KEY = event.keyCode;
if (PRESSED_KEY == keyCodes.UP) {
goToPrevSection();
event.preventDefault();
} else if (PRESSED_KEY == keyCodes.DOWN) {
goToNextSection();
event.preventDefault();
}
}
/*
* When user scrolls with the mouse, we have to change sections
* */
function onMouseWheel(event) {
//Normalize event wheel delta
var delta = event.originalEvent.wheelDelta / 30 || -event.originalEvent.detail;
//If the user scrolled up, it goes to previous section, otherwise - to next section
if (delta < -1) {
goToNextSection();
} else if (delta > 1) {
goToPrevSection();
}
event.preventDefault();
}
/*
* If there's a previous section, section to it
* */
function goToPrevSection() {
console.log($currentSection.prev().length > 0);
if ($currentSection.prev().length) {
goToSection($currentSection.prev());
}
}
/*
* If there's a next section, section to it
* */
function goToNextSection() {
if ($currentSection.next().length > 0) {
goToSection($currentSection.next());
}
}
/*
* Actual transition between sections
* */
function goToSection($section) {
//If the sections are not changing and there's such a section
if (!isAnimating && $section.length) {
//setting animating flag to true
isAnimating = true;
//Sliding to current section
TweenLite.set($currentSection, {
autoAlpha: 0,
display: 'none'
});
$currentSection = $section;
$background.classList = "";
TweenLite.set($currentSection, {
display: 'block'
});
TweenLite.set($background, {
className: 'bg-' + ($sections.index($currentSection) + 1)
});
//console.log($sections.index($currentSection) + 1);
TweenLite.fromTo($background, 0.6, {
scale: 0.9,
autoAlpha: 0
}, {
scale: 1,
autoAlpha: 1,
ease: Power1.easeOut,
onComplete: onSectionChangeEnd,
onCompleteScope: this
});
TweenLite.fromTo($currentSection, 0.6, {
autoAlpha: 0
}, {
autoAlpha: 1,
ease: Power1.easeOut,
});
//Animating menu items
TweenLite.to($navButtons.filter(".active"), 0.5, {
className: "-=active"
});
TweenLite.to($navButtons.filter("[href=#" + $currentSection.attr("id") + "]"), 0.5, {
className: "+=active"
});
}
}
/*
* Once the sliding is finished, we need to restore "isAnimating" flag.
* You can also do other things in this function, such as changing page title
* */
function onSectionChangeEnd() {
isAnimating = false;
}
/*
* When user resize it's browser we need to know the new height, so we can properly align the current section
* */
function onResize(event) {
//This will give us the new height of the window
var newPageHeight = $window.innerHeight();
/*
* If the new height is different from the old height ( the browser is resized vertically ), the sections are resized
* */
if (pageHeight !== newPageHeight) {
pageHeight = newPageHeight;
//This can be done via CSS only, but fails into some old browsers, so I prefer to set height via JS
TweenLite.set([$sectionsContainer, $sections], {
height: pageHeight + "px"
});
//The current section should be always on the top
TweenLite.set($sectionsContainer, {
scrollTo: {
y: pageHeight * $currentSection.index()
}
});
}
}
body,
div,
p {
margin: 0;
padding: 0;
}
body {
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
font-weight: 300;
letter-spacing: 0.0625em;
background-color: #000;
}
h1 {
color: #fff;
}
.sections-container {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 10;
}
.section {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
display: none;
visibility: hidden;
opacity: 0;
}
#section-1 {
display: block;
visibility: visible;
opacity: 1;
}
.section .centered h1 {
text-align: center;
}
.section .centered p {
text-align: center;
}
#background {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
display: block;
visibility: visible;
opacity: 1;
}
.bg-1 {
background-color: #5A4748;
}
.bg-2 {
background-color: #45959b;
}
.bg-3 {
background-color: #778899;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenLite.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/plugins/CSSPlugin.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/plugins/ScrollToPlugin.min.js"></script>
<div id="background"></div>
<div class="sections-container">
<div class="section" id="section-1">
<div class="centered">
<h1>1</h1>
</div>
</div>
<div class="section" id="section-2">
<div class="centered">
<h1>2</h1>
</div>
</div>
<div class="section" id="section-3">
<div class="centered">
<h1>3</h1>
</div>
</div>
</div>

How to fix v-align styling for imgs with different sizes in Vue.js transition-group?

I prepared example of image slider what I need.
I encounter with styling issue when using images with various dimensions. When element leaving the array, its location is set as absolute value, what is necessary for smooth transition, tho, but the image is also moved up.
I would like to have nice vertical align into middle even leave or enter the array, but could not get on any way.
Another issue, what I would like to solve is when left the window and then went back after a while. The animation running all cycles at once to reach current state instead just stop animation and run after. Maybe it is my responsibility, but browsers doesn't offer nice event to catch blur window or am I wrong?
According to this discussion
Thank you for any ideas.
let numbers = [{key:1},{key:2},{key:3},{key:4},{key:5},{key:6},{key:7}]
let images = [
{ key:1,
src:"http://lorempixel.com/50/100/sports/"},
{ key:2,
src:"http://lorempixel.com/50/50/sports/"},
{ key:3,
src:"http://lorempixel.com/100/50/sports/"},
{ key:4,
src:"http://lorempixel.com/20/30/sports/"},
{ key:5,
src:"http://lorempixel.com/80/20/sports/"},
{ key:6,
src:"http://lorempixel.com/20/80/sports/"},
{ key:7,
src:"http://lorempixel.com/100/100/sports/"}
]
new Vue({
el: '#rotator',
data: {
items: images,
lastKey: 7,
direction: false
},
mounted () {
setInterval(() => {
if (this.direction) { this.prevr() } else { this.nextr() }
}, 1000)
},
methods: {
nextr () {
let it = this.items.shift()
it.key = ++this.lastKey
this.items.push(it)
},
prevr () {
let it = this.items.pop()
it.key = ++this.lastKey
this.items.unshift(it)
}
}
})
.litem {
transition: all 1s;
display: inline-block;
margin-right: 10px;
border: 1px solid green;
background-color: lightgreen;
padding: 10px 10px 10px 10px;
height: 100px;
}
.innerDiv {
border: 1px solid red;
}
.container {
overflow: hidden;
white-space: nowrap;
height: 250px;
border: 1px solid blue;
background-color: lightblue;
}
.list-enter {
opacity: 0;
transform: translateX(40px);
}
.list-leave-to {
opacity: 0;
transform: translateX(-40px);
}
.list-leave-active {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.11/vue.js"></script>
<div id="rotator">
<button #click="direction = !direction">
change direction
</button>
<transition-group
name="list"
tag="div"
class="container">
<div
v-for="item in items"
:key="item.key" class="litem">
<!--
<div
class='innerDiv'>
{{ item.key }}
</div>
-->
<div class='innerDiv'>
<img :src='item.src'>
</div>
</div>
</transition-group>
</div>
It tooks a while but on the end I think that I have got better result for sliding animation with changing direction feature.
One annoying think is when I swithing the sliding direction so animation is for a 'microsecond' changed to next state and than return to correct one, after it the animation continue as expected. It is happening only in one direction and I don't know how to fix it. Also last box behave differently too only once. No clue at all.
So just 98% solution :-)
let images = [
{key:1, domKey:1, src:"http://lorempixel.com/50/100/sports/" },
{key:2, domKey:2, src:"http://lorempixel.com/50/50/sports/" },
{key:3, domKey:3, src:"http://lorempixel.com/100/50/sports/" },
{key:4, domKey:4, src:"http://lorempixel.com/20/30/sports/" },
{key:5, domKey:5, src:"http://lorempixel.com/80/20/sports/" },
{key:6, domKey:6, src:"http://lorempixel.com/20/80/sports/" },
{key:7, domKey:7, src:"http://lorempixel.com/100/100/sports/" }
]
let setPositionRelative = el => el.style.position = "relative"
new Vue({
el: '#rotator',
data: {
items: images,
lastKey: 7,
direction: true,
changeDirectionRequest: false
},
mounted () {
Array.from(this.$el.querySelectorAll("div[data-key]")).map(setPositionRelative)
setInterval(() => {
if(this.changeDirectionRequest) {
this.changeDirectionRequest = false
this.direction = !this.direction
if (this.direction)
Array.from(this.$el.querySelectorAll("div[data-key]")).map(setPositionRelative)
else
Array.from(this.$el.querySelectorAll("div[data-key]")).map(el => el.style.position = "")
}
if (this.direction) this.prevr()
else this.nextr()
}, 1000)
},
methods: {
nextr () {
let it = this.items.shift()
it.key = ++this.lastKey
this.items.push(it)
},
prevr () {
let it = this.items.pop()
it.key = ++this.lastKey
this.items.unshift(it)
setPositionRelative(this.$el.querySelector("div[data-domkey='"+it.domKey+"']"))
}
}
})
.container {
overflow: hidden;
white-space: nowrap;
height: 200px;
border: 1px solid blue;
background-color: lightblue;
display: flex;
align-items: center;
}
.innerDiv {
border: 1px solid red;
width: auto;
height: auto;
display:-moz-box;
-moz-box-pack:center;
-moz-box-align:center;
display:-webkit-box;
-webkit-box-pack:center;
-webkit-box-align:center;
display:box;
box-pack:center;
box-align:center;
}
.litem {
transition: all 1s;
margin-right: 10px;
border: 1px solid green;
background-color: lightgreen;
padding: 10px 10px 10px 10px;
}
.list2-enter, .list-enter {
opacity: 0;
transform: translateX(40px);
}
.list2-leave-to, .list-leave-to {
opacity: 0;
transform: translateX(-40px);
}
.list-leave-active {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.11/vue.js"></script>
<div id="rotator">
<button #click="changeDirectionRequest = true">change direction</button>
<transition-group name="list" tag="div" class="container">
<div v-for="item in items"
:key="item.key"
:data-domkey="item.domKey"
class="litem">
<div class='innerDiv'>
<img :src='item.src'>
</div>
</div>
</transition-group>
</div>

How to bind DOM property in directive

Given this example:
var SomeApp = angular.module('SomeApp', [])
.controller('SomeController', function($scope){
$scope.items = [0,1,2,3]
})
.directive('gridResize', function(){
return {
scope: true,
link: function(scope, elem) {
scope.gridResize = {
width: $(elem).width(),
height: $(elem).height()
};
}
}
})
.parent {
width: 80%;
position: relative;
left: 50%;
transform: translateX(-50%);
border: 1px solid red;
}
.parent > * {
background-color: rgba(0,0,0,.14);
margin-bottom: 1px;
padding: 20px;
box-sizing: border-box;
}
.parent > *:last-child {
margin-bottom: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="SomeApp">
<div class="parent" ng-controller="SomeController" grid-resize>
<div ng-style="{'min-height':($parent.gridResize.width/8) + 'px'}"
ng-repeat="item in items"
>
height: {{$parent.gridResize.height}} | width: {{$parent.gridResize.width}}
</div>
</div>
</div>
Can anyone tell me how I could bind the height and width of the grid-resize directive to the DOM element? I want the angular properties to change when the DOM element changes.
In your directive use the window "resize" event to update the sizes on the scope:
var SomeApp = angular.module('SomeApp', [])
.controller('SomeController', function($scope){
$scope.items = [0,1,2,3]
})
.directive('gridResize', function(){
return {
scope: false,
link: function(scope, elem) {
scope.gridResize = {
width: $(elem).width(),
height: $(elem).height()
};
angular.element(window).on('resize', function(e) {
scope.gridResize = {
width: $(elem).width(),
height: $(elem).height()
};
scope.$apply();
});
}
}
})
Also notice that I changed the directive's scope to 'false'. You already have a scope on that element created by the ng-controller directive.

jQuery UI Droppable block by absolute div

How can a jquery ui droppable be blocked by an overlaid div?
See this example: http://jsfiddle.net/JSFU4/3/. A red div is overlaying the droppable area; however, when dragging on top of this area, the droppable is still activated. How can this be avoided?
Html
<div>
<div class="drop">drop here</div>
</div>
<div>
<div class="drag">drag me</div>
</div>
<div class="overlay">i want to block the droppable</div>
CSS
.drag {
display: inline-block;
width: 100px;
height: 100px;
border: 1px solid black;
background-color: lightblue;
z-index: 1;
}
.drop {
display: inline-block;
border: 1px dotted black;
width: 200px;
height: 200px;
}
.drop-hover {
background-color: grey;
}
.overlay {
position: absolute;
width: 300px;
height: 100px;
top: 50px;
background-color: red;
border: 1px solid black;
}
JS
$(function () {
$('.drag').draggable();
$('.drop').droppable({
tolerance: 'pointer',
hoverClass: 'drop-hover'
});
});
I don't think there is anything out of box to achieve this. You need to do some kind of collision detection and then do your logic accordingly. I have done a POC for you.
DEMO
var drag = $('.drag');
var overlay = $('.overlay');
$(function () {
$('.drag').draggable();
$('.drop').droppable({
tolerance: 'pointer',
hoverClass: 'drop-hover'
});
});
var int = self.setInterval(function () {
if (overlaps(drag, overlay)) {
$('.drop').css('visibility', 'hidden');
} else {
$('.drop').css('visibility', 'visible');
}
}, 100);
var overlaps = (function () {
function getPositions(elem) {
var pos, width, height;
pos = $(elem).position();
width = $(elem).width() / 2;
height = $(elem).height();
return [[pos.left, pos.left + width], [pos.top, pos.top + height]];
}
function comparePositions(p1, p2) {
var r1, r2;
r1 = p1[0] < p2[0] ? p1 : p2;
r2 = p1[0] < p2[0] ? p2 : p1;
return r1[1] > r2[0] || r1[0] === r2[0];
}
return function (a, b) {
var pos1 = getPositions(a),
pos2 = getPositions(b);
return comparePositions(pos1[0], pos2[0]) && comparePositions(pos1[1], pos2[1]);
};
})();
The code above detects the collision between two DIVs and returns true or false.

Resources