Fire event at Infinite horizontal scroll end in ionic 5 - css

I am trying to build infinite scrolling content horizontally. Following code does the job on the first load.
HTML:
<div class="thumnails">
<div class="list-thumbnail">
<div class="img-thumb" *ngFor="let user of userList">
<ion-grid>
<ion-row>
{{user.name}}
</ion-row>
</ion-grid>
</div>
</div>
</div>
SCSS:
.thumnails{
overflow-x: scroll;
overflow-y: hidden;
.list-thumbnail{
height: 100%;
white-space: nowrap;
.img-thumb{
display: inline-block;
border: 1px solid #ddd;
border-radius: 4px;
padding: 3px;
}
}
}
::-webkit-scrollbar {
display: none;
}
After this, I need to fire a method when a user reaches the end on scroll. Ionic has the component ion-infinite-scroll which works in vertical scroll but it's not firing when I use it with the above code.
Is there a way to fire an event at the end of the horizontal scroll?

I suggest trying the ionScroll event from ion-content component.
Enable ionScroll like:
<ion-content [scrollEvents]="true" (ionScroll)="logScrolling($event)">
On logScrolling check to see if the scroll reached a certain point. If it did, add more data to your infinite scrolling content:
async logScrolling($event) {
if($event.target.localName != "ion-content") {
return;
}
const scrollElement = await $event.target.getScrollElement();
const scrollWidth = scrollElement.scrollWidth - scrollElement.clientWidth;
const threshold = 50;
if(scrollWidth < threshold) {
//...load data here
}}
I did not test it but it should work like that. Please let me know if you ran into any problems.

Here is my solution. Is not beautiful and I did not test it for performance issues but it does exactly what you want
HTML:
<ion-content>
<div class="thumnails" (scroll)="scrollEv($event)">
<div class="list-thumbnail">
<div class="img-thumb" *ngFor="let user of userList">
<span class="id" hidden>{{ user.id }}</span>
<ion-grid>
<ion-row>
{{ user.name }}
</ion-row>
</ion-grid>
</div>
</div>
</div>
</ion-content>
TS:
userList: Array<any> = [];
// elements count till the end of list to trigger the `getData()` function
elementsToTrigger: number = 10;
constructor() { }
ngOnInit() {
this.getData();
}
scrollEv(ev) {
// get the last visible on screen element of the list by it's coordenates:
// x => the offsetWidth of the body - 5px so it doesn't get out of boundaries
// y => distance the list is from the top. Note that here the list is fixed at the top because it's the only element on screen, but if, at some point,
// it will scroll or be placed at someplace else you should get this distance manually before this next line.
let elByCoords = document.elementFromPoint(document.body.offsetWidth - 5, 0);
let elId = elByCoords.children[0].textContent;
// check if the last element visible on screen is the "elementsToTrigger'th to last" and if so get more data
// here you should also check if all data has been loaded so you don't keep sending requests to your backend
if ((this.userList.length - this.userList.findIndex(user => user.id === +elId)) <= this.elementsToTrigger) {
this.getData();
}
}
getData() {
// here goes your logic to retrieve more results from database.
let startAt: number = this.userList?.length || 0;
let arr = [];
for (let i = startAt; i < startAt + 50; i++) {
arr.push({ id: i, name: `User ${i}` });
}
this.userList = (this.userList || []).concat(arr);
}
You may need to play a little with document.elementFromPoint(x, y) to find the correct values

Related

vuetify enlarge image on click

i have a image gallery on vuetify and trying to enlarge the image using the fullscreen function.
<template>
<v-container class="mt-12">
<v-row justify="center" v-if="!$fetchState.pending">
<v-col
cols="4"
v-for="image in service.data.attributes.gallery.data"
:key="image.id"
>
<v-img
class="d-flex child-flex"
:src="`${image.attributes.formats.large.url}`"
:lazy-src="`${image.attributes.formats.thumbnail.url}`"
contain
#click="toggleFullscreen(image.attributes.url)"
>
</v-img>
</v-col>
</v-row>
<v-row v-else>
<LoadSpinner />
</v-row>
and my toogle method
methods: {
toggleFullscreen(elem) {
console.log(elem);
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.msRequestFullscreen) {
elem.msRequestFullscreen();
} else if (elem.mozRequestFullScreen) {
elem.mozRequestFullScreen();
} else if (elem.webkitRequestFullscreen) {
elem.webkitRequestFullscreen();
} else {
alert(
"Sorry, your browser is too old and doesn't support fullscreen :-("
);
}
},
},
i have and array coming from a strapi api, the idea is show in the gallery a low res image and when cliking fullscreen the high res version.
problem everytime i click it triggers the alert, so the method is not getting the correct info to work, could be v-img creates a div and uses de image as background?
thanks
pd tried already dialog and overlay but can't figure out how to "attack" individual elements
After trying various libraries and what had to offer vuetify i did a workaround with vanilla css.
I created a variable, when you click on a image passes the url of the image, the template loads a div when this variable is not null with and overlay class to cover the screen.
in my template
<v-img
v-for="image in service.data.attributes.gallery.data"
:key="image.id"
:src="`${image.attributes.formats.large.url}`"
#click="toggleFullscreen(image.attributes.url)"
/>
<div v-if="selectedImage" class="overlay">
<v-img
class="mt-12"
:src="selectedImage"
alt=""
height="95vh"
contain
dark
#click.stop="selectedImage = null"
>
</v-img>
</div>
script part
<script>
...
export default {
data() {
return {
...
selectedImage: null,
};
},
...
methods: {
toggleFullscreen(elem) {
this.selectedImage = elem;
},
},
};
</script>
css
.overlay {
position: fixed; /* Sit on top of the page content */
width: 100%; /* Full width (cover the whole page) */
height: 100%; /* Full height (cover the whole page) */
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 2; /* Specify a stack order in case you're using a different order for other elements */
cursor: pointer; /* Add a pointer on hover */
}

CodeMirror Line-Break doesn't add line number - Angular

I'm using code mirror from ngx-codemirror. I want to split the line when it fits to the width of the parent. I have found some solutions to split the like using,
lineWrapping: true
and in styles
.CodeMirror-wrap pre {
word-break: break-word;
}
Using this I was able to split the line but I need to show the line number too.
The line number is not shown for the line that was just split.
This is the stackblitz link to my issue : code-mirror-line-break-issue
Screenshot :
Please help me with this.
This is not feasible using Code Mirror options, as this is something that is a bit counter intuitive that is rarely (ever?) wanted.
Like I said in my comment, say 2 persons discussing on a phone/web chat about a piece of code/json. They will not see the same thing when one mentions a line number to the other if they have different windows/screen sizes
Solution
As a hack, you can create your own elements representing line numbers and place them over the default line numbers.
Here is the stackblitz demo
Note: This a a very basic example. If you change code mirror settings (font size, gutters,...), you might need to tweak the css or do more calculation based on these settings.
component.html
<div class='codeMirrorContainer'>
<ngx-codemirror
#codeMirror
[options]="codeMirrorOptions"
[(ngModel)]="codeObj"
></ngx-codemirror>
<ul class='lineContainer' [style.top.px]="-topPosition">
<li [style.width.px]='lineWidth' *ngFor="let line of lines">{{line}}</li>
</ul>
</div>
component.css
li
{
height: 19px;
list-style: none;
}
.codeMirrorContainer
{
position:relative;
overflow: hidden;
}
.lineContainer
{
position:absolute;
top:0;
left:0;
margin: 0;
padding: 5px 0 0 0;
text-align: center;
}
::ng-deep .CodeMirror-linenumber
{
visibility: hidden; /* Hides default line numbers */
}
component.ts
export class AppComponent
{
#ViewChild('codeMirror') codeMirrorCmpt: CodemirrorComponent;
private lineHeight: number;
public lineWidth;
public topPosition: number;
public lines = [];
codeMirrorOptions: any = ....;
codeObj :any = ...;
constructor(private cdr: ChangeDetectorRef)
{
}
ngAfterViewInit()
{
this.codeMirrorCmpt.codeMirror.on('refresh', () => this.refreshLines());
this.codeMirrorCmpt.codeMirror.on('scroll', () => this.refreshLines());
setTimeout(() => this.refreshLines(), 500)
}
refreshLines()
{
let editor = this.codeMirrorCmpt.codeMirror;
let height = editor.doc.height;
this.lineHeight = editor.display.cachedTextHeight ? editor.display.cachedTextHeight : this.lineHeight;
if (!this.lineHeight)
{
return;
}
let nbLines = Math.round(height / this.lineHeight);
this.lines = Array(nbLines).fill(0).map((v, idx) => idx + 1);
this.lineWidth = editor.display.lineNumWidth;
this.topPosition = document.querySelector('.CodeMirror-scroll').scrollTop;
this.cdr.detectChanges();
}
}

How to remove the DOM elements of a modal on close

Background: At time of writing, Fomantic-UI is the live-development fork of Semantic-UI which will one day be rolled into Semantic-UI and is for the mean time the de facto supported genus of Semantic-UI.
Issue: Fomantic-UI provides a modal capability - just call .modal() on a JQuery element et voila. However, when the modal is closed, the DOM elements for the modal element remain hidden in the DOM. My use case requires removal of those elements, but I am concerned about the 'left over' wiring for the modal capability.
Research done: The FUI documentation on modals is useful but is written from the perspective of getting a modal up, but not cleanly taking it down.
Reproduction: The snippet below is a code-based approach to creation of a modal and wiring for the button listeners. Click the button to open the modal, then click the close button. Two seconds later a simple JQuery count of DOM modal elements remaining will be shown - it should be zero but will be 1.
Notes:
1 - FUI modal has a frustrating feature of auto-closing the modal when any button is clicked. It has one saving clause which is that if the triggered button event handler returns false then the modal stays open. Clearly, if you are doing form validation etc, you need to know this. Additionally, if you prefer to override the default feature for all buttons, return false from the onHide function, e.g.
element.modal('setting', 'onHide', function(){ return false; });
element.modal('show');
2 - This snippet is using the the 'dist' versions of FUI. Since FUI changes often, the snippet may fail if there have been breaking changes by the time you see it. At time of writing the official release on jsdelivr cdn is 2.8.3. (Edited 17-Jan-2020).
var fnSpecial = function(){
console.log('Close button click function - return true to hide');
// ... do function activity here....
return true;
}
$('#b1').on('click', function(){
makeModal({
title: 'I be modal',
content: 'Modal be I !',
actions: [
{ text: 'Close', fn: fnSpecial}, // for more buttons in the modal add more entries here.
]
})
})
function makeModal(opts){
// create your modal element - I grab the modal template
var tplt = $('#modalTemplate').html();
// defaults for modal create
var obj = {
title: 'No title !',
content: 'No content',
actions: [
]
}
// Merge the above defaults with the user-supplied options
obj = $.extend(obj, opts);
// Apply modal options to the soon-to-be modal element
var ele = $(tplt);
ele.find('.modalHeading').html(obj.title);
ele.find('.modalBody').html(obj.content);
ele.addClass('modalContentCopy');
var modalButtons = ele.find('.modalButtons');
for (var i =0, max = obj.actions.length; i < max; i = i + 1 ){
var btn = $('<button >' + obj.actions[i].text + '</button>');
var fn = obj.actions[i].fn;
btn.data('fn', fn); // store the callback on the element to avoid closures.
btn.appendTo(modalButtons);
btn.on('click', function(e){
var fn = $(this).data('fn');
if (typeof fn === "function"){
var hide = fn(); // IMPORTANT: the function triggered for the button must return true to close the modal or false to keep it open !
console.log('Button says hide=' + hide)
if (hide){
ele.modal('destroy');
ele.modal('hide');
$('#info')
.html('***');
// wait 2 secs and see if the DOM element has gone
setTimeout( function(){
var num = $('.modalContentCopy').length;
$('#info')
.html(num)
.css({ backgroundColor: (num === 0 ? 'lime' : 'red')});
}, 2000);
}
}
});
}
// Simple example of how to reveal the modal element as a f-ui modal
ele
.appendTo($('body'))
.modal('setting', 'transition', "vertical flip")
.modal('setting', 'onHide', function(){ return false; }) // stop the auto-closing by FUI
.modal('show'); // finally show the modal
}
p {
padding: 10px;
}
.modalContent {
border: 1px solid lime;
margin: 5px;
padding: 5px;
}
.modalHeading {
border: 1px solid cyan;
margin: 5px;
padding: 5px;
}
.modalBody {
border: 1px solid magenta;
margin: 5px;
padding: 5px;
}
.modalContent {
background-color: white;
}
.ipt {
margin: 5px 20px;
display: block;
}
<link href="https://fomantic-ui.com/dist/semantic.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://fomantic-ui.com/dist/semantic.js"></script>
<body>
<p>Click the 'show modal' button to open a modal, then the 'Close' button to close it. See console for messages. The critical point is the final display of the modal artefact count which appears after a 2 second delay, allowing for transitions to complete. We want a zero - what will it be?
</p>
<p>
<button id='b1'>Show a modal</button> <span>Count of DOM artifacts after close should be zero - value is >>>>> </span><span id='info'>[value will appear here after modal close]</span>
</p>
<div id='modalTemplate' style='display: none;'>
<div class='modalContent'>
<div class='modalHeading'>
I am the heading
</div>
<div class='modalBody'>
I am the body
</div>
<div class='modalButtons'>
</div>
</div>
</div>
</body>
The answer is to use the currently undocumented features of modal('destroy') and modal('remove') after the closing animation completes.
.modal('setting', 'onVisible', function(){
ele
.on("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function(e){
console.log('Animation complete')
$(this).off(e); // remove this listener
$(this).modal('destroy'); // take down the modal object
$(this).remove(); // remove the modal element, at last.
});
})
If you do not observe the completion of the closing transition then FUI throws a warning about transitions on missing elements.
Modal.Destroy clears out the modal object that FUI attaches to the JQuery modal element. The ele.remove() is the standard JQuery remove function. The result is, as per my requirement, the element previously made modal is removed from the DOM.
There will be one artefact which is the FUI dimmer div, but my testing to date shows that this is not an issue, meaning they are not visible and do not rack up over time.
See snippet below for working example. Look at the end of the JS section for the solution.
var fnSpecial = function(){
console.log('Close button click function - return true to hide');
// ... do function activity here....
return true;
}
$('#b1').on('click', function(){
makeModal({
title: 'I be modal',
content: 'Modal be I !',
actions: [
{ text: 'Close', fn: fnSpecial}, // for more buttons in the modal add more entries here.
]
})
})
function makeModal(opts){
// create your modal element - I grab the modal template
var tplt = $('#modalTemplate').html();
// defaults for modal create
var obj = {
title: 'No title !',
content: 'No content',
actions: [
]
}
// Merge the above defaults with the user-supplied options
obj = $.extend(obj, opts);
// Apply modal options to the soon-to-be modal element
var ele = $(tplt);
ele.find('.modalHeading').html(obj.title);
ele.find('.modalBody').html(obj.content);
ele.addClass('modalContentCopy');
var modalButtons = ele.find('.modalButtons');
for (var i =0, max = obj.actions.length; i < max; i = i + 1 ){
var btn = $('<button >' + obj.actions[i].text + '</button>');
var fn = obj.actions[i].fn;
btn.data('fn', fn); // store the callback on the element to avoid closures.
btn.appendTo(modalButtons);
btn.on('click', function(e){
var fn = $(this).data('fn');
if (typeof fn === "function"){
var hide = fn(); // IMPORTANT: the function triggered for the button must return true to close the modal or false to keep it open !
console.log('Button says hide=' + hide)
if (hide){
ele.modal('destroy');
ele.modal('hide');
$('#info')
.html('***');
// wait 2 secs and see if the DOM element has gone
setTimeout( function(){
var num = $('#theBody').find('.modalContentCopy').length;
$('#info')
.html(num)
.css({ backgroundColor: (num === 0 ? 'lime' : 'red')});
}, 2000);
}
}
});
}
// Simple example of how to reveal the modal element as a f-ui modal
ele
.appendTo($('body'))
.modal('setting', 'transition', "vertical flip")
.modal('setting', 'onHide', function(){ return false; }) // stop any button closing the modal
// <solution starts>
.modal('setting', 'onVisible', function(){
ele
.on("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function(e){
console.log('Animation complete')
$(this).off(e);
$(this).modal('destroy');
$(this).remove();
});
})
// <solution ends>
.modal('show'); // finally show the modal
}
p {
padding: 10px;
}
.modalContent {
border: 1px solid lime;
margin: 5px;
padding: 5px;
}
.modalHeading {
border: 1px solid cyan;
margin: 5px;
padding: 5px;
}
.modalBody {
border: 1px solid magenta;
margin: 5px;
padding: 5px;
}
.modalContent {
background-color: white;
}
.ipt {
margin: 5px 20px;
display: block;
}
<link href="https://fomantic-ui.com/dist/semantic.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://fomantic-ui.com/dist/semantic.js"></script>
<body id='theBody'>
<p>Click the 'show modal' button to open a modal, then the 'Close' button to close it. See console for messages. The critical point is the final display of the modal artefact count which appears after a 2 second delay, allowing for transitions to complete. We want a zero - what will it be?
</p>
<p>
<button id='b1'>Show a modal</button> <span>Count of DOM artifacts after close should be zero - value is >>>>> </span><span id='info'>[value will appear here after modal close]</span>
</p>
<div id='modalTemplate' style='display: none;'>
<div class='modalContent'>
<div class='modalHeading'>
I am the heading
</div>
<div class='modalBody'>
I am the body
</div>
<div class='modalButtons'>
</div>
</div>
</div>
</body>

image slider multiple rows with just css or angularjs?

Is there a way to create an multiple row image slider like the one in the image below using just css? or is there a way to do this with angular?
The slider needs to move as one (single rows cannot be swiped individually).
First you need to understand the overflow property in css:
https://css-tricks.com/almanac/properties/o/overflow/
This will allow you to see there is a scroll property. That can make your scroll bars. Yours should use overflow-x to scroll the direction you want it to go.
As for angular, you need to look into ng-repeat command. Here is a fiddle that is doing what you are looking for:
<div ng-repeat="user in users | limitTo:display_limit">
http://jsfiddle.net/bmleite/hp4w7/
Quick answer to your question.. no, there is no way to do this with just CSS because you will have to handle the swipe, touch, click, etc. events using javascript. I guess I was working under the assumption that you would be adding angularjs into your application solely for this purpose, so I made a jQuery solution. If that is a wrong assumption, I will rewrite an angular solution.
Basically, the idea is that you structure your HTML/CSS in a way to get the effect of the sliding within a given container, and then use event handlers to update the slider as the user interacts with it.
Working DEMO
HTML
<div class="slider-display centered">
<div class="image-container">
<div class="image">Image<br>1</div>
<div class="image">Image<br>2</div>
<div class="image">Image<br>3</div>
<div class="image">Image<br>4</div>
<div class="image">Image<br>5</div>
<div class="image">Image<br>6</div>
<div class="image">Image<br>7</div>
<div class="image">Image<br>8</div>
<div class="image">Image<br>9</div>
<div class="image">Image<br>10</div>
<div class="image">Image<br>11</div>
<div class="image">Image<br>12</div>
<div class="image">Image<br>13</div>
<div class="image">Image<br>14</div>
<div class="image">Image<br>15</div>
<div class="image">Image<br>16</div>
<div class="image">Image<br>17</div>
<div class="image">Image<br>18</div>
</div>
</div>
<div class="centered" style="text-align: center; max-width: 350px;">
<button class="move-left"><--</button>
<button class="move-right">--></button>
</div>
Javascript
$(function () {
var getWidth = function ($element) {
var total = 0;
total += $element.width();
total += Number($element.css("padding-left").replace("px", ""));
total += Number($element.css("padding-right").replace("px", ""));
total += Number($element.css("border-left").split("px")[0]);
total += Number($element.css("border-right").split("px")[0]);
total += Number($element.css("margin-left").split("px")[0]);
total += Number($element.css("margin-right").split("px")[0]);
return total;
};
var sliderPosition = 0;
var imageWidth = getWidth($(".image").eq(0));
$(".move-left").on("click.slider", function () {
var maxVisibleItems = Math.ceil($(".slider-display").width() / imageWidth);
var maxItemsPerRow = Math.ceil($(".image-container").width() / imageWidth);
var numRows = Math.ceil($(".image-container .image").length / maxItemsPerRow);
var maxPosition = numRows > 1 ? maxVisibleItems - maxItemsPerRow : maxVisibleItems - $(".image-container .image").length;
if (sliderPosition > (maxPosition)) {
sliderPosition--;
var $imageContainer = $(".image-container");
$(".image-container").animate({
"margin-left": sliderPosition * imageWidth
},{
duration: 200,
easing: "linear",
queue: true,
start: function () {
$(".move-left").prop("disabled", true);
},
done: function () {
$(".move-left").prop("disabled", false);
}
});
}
});
$(".move-right").on("click.slider", function () {
if (sliderPosition < 0) {
sliderPosition++;
var $imageContainer = $(".image-container");
$(".image-container").animate({
"margin-left": sliderPosition * imageWidth
},{
duration: 200,
easing: "linear",
queue: true,
start: function () {
$(".move-right").prop("disabled", true);
},
done: function () {
$(".move-right").prop("disabled", false);
}
});
}
});
});
CSS
.image {
float: left;
height: 80px;
width: 80px;
background: #888888;
text-align: center;
padding: 5px;
margin: 5px;
font-size: 1.5rem;
}
.image-container {
width: 650px;
position: relative;
}
.slider-display {
max-width: 450px;
overflow: hidden;
background: #ddd
}
.centered {
margin: 0 auto;
}

Vertical scroll inside paper-header-panel behaving inappropriately

I am currently working on a page. I have used polymer's paper-drawer-panel and paper-header-panel combination.
Basically there are two containers, the inner container needs to be scrolled horizontally while the outer container needs to be scrolled vertically. When I have not used the paper-header-panel, the behavior is appropriate. You can see the demo here. If you open this site on a mobile browser (chrome or others/haven't checked with Safari) or on the mobile simulator in chrome, I can scroll both horizontally as well as vertically. This is the behavior I have expected.
But, when I add in the paper-header-panel (part of my custom element <app-layout>), I am able to scroll horizontally (in a mobile browser) but when I touch an element present in the horizontally scrollable div and try to scroll it vertically as in the previous case, the vertical scroll doesn't work anymore. The demo is present here.
The relevant source code for the app-layout element is as below.
html -
<dom-module id="app-layout">
<link rel="import" type="css" href="app-layout.css">
<template>
<paper-drawer-panel id="drawerPanel" responsive-width="1024px" drawer-width="280px">
<paper-header-panel class="list-panel" drawer>
<!-- List Toolbar -->
<div class="paper-header has-shadow layout horizontal center" id="navheader">
</div>
<!-- Menu -->
<div class="left-drawer">
<paper-menu class="list" selected="0" on-iron-activate="_listTap">
<template is="dom-repeat" items="{{menus}}">
<paper-item role="menu"><iron-icon class="menuitems" icon$={{item.icon}}></iron-icon><span>{{item.label}}</span></paper-item>
</template>
</paper-menu>
</div>
</paper-header-panel>
<paper-header-panel class$="{{positionClass}}" main mode="{{mainMode}}">
<!-- Main Toolbar -->
<paper-toolbar class$="{{toolbarClass}}">
<paper-icon-button icon="menu" paper-drawer-toggle></paper-icon-button>
<div style="width:60px" id="app-image"><iron-image style="width:40px; height:40px; background-color: lightgray;"
sizing="contain" preload fade src= "/images/app-icon-110.png"></iron-image></div>
<div hidden$="{{_isMobile}}" class="flex">{{label}}</div>
<div class="flex"></div>
<paper-icon-button icon="search" on-tap="toggleSearch"></paper-icon-button>
<paper-icon-button icon="more-vert"></paper-icon-button>
</paper-toolbar>
<div class="content">
<paper-material>
<content select=".main-content"></content>
</paper-material>
</div>
</paper-header-panel>
</paper-drawer-panel>
</template>
</dom-module>
CSS -
* {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
:host {
width: 100%;
height: 100%;
-moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box;
display: block;
}
#drawerPanel {
--paper-drawer-panel-left-drawer-container: {
background-color: #eee;
};
}
paper-header-panel {
background-color: #eee;
}
paper-toolbar {
background-color: #00bcd4;
}
.left-drawer {
background-color: #eee;
}
paper-header-panel .content {
height: 100%;
}
paper-header-panel[mode=cover] .content {
padding: 0 90px 0 0px;
height: 100%;
}
paper-header-panel {
--paper-header-panel-cover-container: {
height: 100%;
left: 90px;
};
}
paper-header-panel[mode=cover] paper-toolbar {
color: #fff;
font-size: 20px;
font-weight: 400;
padding-right: 16px;
}
.paper-header-panel paper-toolbar #app-image {
margin-left: -15px;
}
paper-material {
overflow-y: auto;
height: auto;
background-color: #fff;
z-index: 1;
}
paper-header-panel[mode=cover] paper-material {
max-width: 1024px;
margin: 64px auto;
}
From our Comments,
As an alternative solution until you find the problem you can test using Iscroll 5 for the horizontal scroll of the cards and see if it works ok within the App.
Ive used Iscroll 5 for my App using both horizontal and vertical scrolls with 100's of Items and its fast on Polymer. I haven't had any performance issues so far although i turned off bounce in iscroll options eg ,bounce: false to get that extra performance boost
If you have Click events for the Cards then add ,click:true to the Iscroll options
Iscroll 5 guide here http://iscrolljs.com/
The demo creates the Iscroll for any row when the user scrolls horizontally there to save resources and the Original Demo in my comments was for JQM framework which has built in Swipe detection.
In Polymer version 0.5 it has built in Touch functionality not sure about version 1 yet but i never used yet https://www.polymer-project.org/0.5/docs/polymer/touch.html
For Polymer i created another Demo that Uses Javascript Touch events to detect horizontal movements only so you wont need any Other Touch gesture Pluggings to add to the App
Demo I set a 5sec delay for the code to initialize. Pretty much all the code is for handling Touch. In the function slide(x) { is the Iscroll code about 10 lines
http://codepen.io/anon/pen/pJRmLo
Code
var slides = 17; //how many items in a row
var totalwidth = slides * 80; //times that by the width of each item in a row
$(".scroller").css("width", totalwidth+"px"); //set the total width of the horizontal wrapper
// touch function
var startPos;
var handlingTouch = false;
var itemis;
setTimeout(function() {
document.addEventListener('touchstart', function(e) {
// Is this the first finger going down?
if (e.touches.length == e.changedTouches.length) {
startPos = {
x: e.touches[0].clientX,
y: e.touches[0].clientY
};
}
});
document.addEventListener('touchmove', function(e) {
// If this is the first movement event in a sequence:
if (startPos) {
// Is the axis of movement horizontal?
if (Math.abs(e.changedTouches[0].clientX - startPos.x) > Math.abs(e.changedTouches[0].clientY - startPos.y)) {
handlingTouch = true;
e.preventDefault();
onSwipeStart(e);
}
startPos = undefined;
} else if (handlingTouch) {
e.preventDefault();
onSwipeMove(e);
}
});
document.addEventListener('touchend', function(e) {
if (handlingTouch && e.touches.length == 0) {
e.preventDefault();
onSwipeEnd(e);
handlingTouch = false;
}
});
function slide(x) {
var cclass = $(itemis).attr("class")
var ccclass = "."+cclass;
var newis = $(itemis).attr("data-id");
if (newis != "running") {
var cclass = new IScroll(ccclass, {
eventPassthrough: true,
scrollX: true,
scrollY: false,
preventDefault: false
});
cclass.scrollBy(-50, 0, 500);
//control here how many pixels to auto scroll uppon activating the scroll eg -50px
$(itemis).attr("data-id","running")
}
}
var swipeOrigin, x, itempos;
function onSwipeStart(e) {
// find what element is been touched. In your case it may be closest("swElement") but you need to test
itemis = $(e.target).closest("div");
// when touching over an element get its target, in this case the closest div of the row
swipeOrigin = e.touches[0].clientX;
}
function onSwipeMove(e) {
x = e.touches[0].clientX - swipeOrigin;
// slide(x);
}
function onSwipeEnd(e) {
//On Touch End if x (distance traveled to the right) is greater than +35 pixels then slide element +100 pixels.
if (x > 35) {
slide(0);
}
else {
slide(0);
}
}
}, 5000);
The above touch function I originally used for transforming/moving list items by touch drag similarly to Gmail App, for JQM and Polymer list items. Can be used for anything horizontally in the case of Iscroll is not really used in that way but it basically says if you touch move horizontally over a row activate the Iscroll for that row
Check my demo in the Link for an alternative use of the function.
jQuery touchSwipe event on element prevents scroll
The problem arises because on mobiles, horizontal scrolling is disabled when vertical scrolling is active and vice-versa. I have solved this for now using Tasos suggestion to use iScroll. I used polymer's async function to initialize the scrollers with a delay. Here is the code I have used as part of polymer-ready.
<script>
Polymer ({
...
...
ready:function() {
this.async(function() {
this.setScroll();
}, null, 300);
},
setScroll: function() {
var nodeList = document.querySelectorAll('.wrapper');
if(nodeList.length == 0) {
this.async(function() { this.setScroll(); }, null, 300);
}
for (var i=0; i<nodeList.length; i++) {
// this.setScrollDirection ('y', nodeList[i]);
nodeList[i].id = 'wrapper'+i;
var myScroll = new IScroll('#'+nodeList[i].id, { eventPassthrough: true, scrollX: true, scrollY: false, preventDefault: false });
// myScroll.scrollBy(-50, 0, 500);
}
}
})();
</script>
I was also able to use Polymers track gesture as mentioned in the documentation.
Listening for certain gestures controls the scrolling direction for
touch input. For example, nodes with a listener for the track event
will prevent scrolling by default. Elements can be override scroll
direction with this.setScrollDirection(direction, node), where
direction is one of 'x', 'y', 'none', or 'all', and node defaults to
this.
state - a string indicating the tracking state:
start - fired when tracking is first detected (finger/button down and moved past a pre-set distance threshold)
track - fired while tracking
end - fired when tracking ends
x - clientX coordinate for event
y - clientY coordinate for event
dx - change in pixels horizontally since the first track event
dy - change in pixels vertically since the first track event
ddx - change in pixels horizontally since last track event
ddy - change in pixels vertically since last track event
hover() - a function that may be called to determine the element currently being hovered
I have used (dx > dy) to understand whether it is a horizontal or vertical swipe and then enabled horizontal and vertical swiping specifically as per the case. This worked but I liked the bounce and the other options provided by iScroll and besides its only 4KB minified and gzipped. So, I decided to go with iScroll.
[Note: jQuery is not needed to use iScroll. It is an independent script]

Resources