vuetify enlarge image on click - css

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 */
}

Related

iFrame not detecting touch or scroll on mobile with 3D scene - Threejs - React Three

I have created a iFrame on my 3D website in which renders a path from my portfolio. I have been attempting to scroll or touch (mobile) this iFrame for a while now, the desktop version works fine, you can scroll, access pages, etc although on mobile, the touch event does not seem to happens.
I have searched several posts and fixes, but none of them made the touch event, or touch scroll from this iframe to work.
You can access my website through https://niltonsf.dev and on the top right select viewWebpage to focus on the html screen.
Here is the component in which renders the iframe:
import { Html } from "#react-three/drei";
import { Dispatch, SetStateAction } from "react";
import { isMobile } from "react-device-detect";
import * as THREE from "three";
interface MonitorProps {
geometry: THREE.BufferGeometry;
screen: THREE.Mesh;
bakedTexture: THREE.Texture;
setIsPointerOnHtml: Dispatch<SetStateAction<boolean>>;
isFocusOnHtml: boolean;
}
export default function Monitor({
geometry,
screen,
bakedTexture,
setIsPointerOnHtml,
isFocusOnHtml,
}: MonitorProps) {
return (
<>
<primitive object={screen}>
<group position={[-2.57, 1.8, -0.01]} rotation-y={1.565}>
<Html
transform
prepend
wrapperClass="htmlScreen"
scale={0.35}
distanceFactor={1.17}
zIndexRange={[0, 0]}
>
<div
onClick={(e) => {
if (!isFocusOnHtml) e.preventDefault();
}}
onPointerEnter={(e) => {
if (isFocusOnHtml) setIsPointerOnHtml(true);
}}
onPointerLeave={(e) => {
if (isFocusOnHtml) setIsPointerOnHtml(false);
}}
>
<iframe
id="iframe"
src="https://niltonsf.dev/static"
// src="http://192.168.1.13:3000/static"
title="myStaticWebsite"
style={{
width: isMobile ? 1200 : 1500,
}}
/>
</div>
</Html>
<mesh>
<planeGeometry args={[1.535, 0.69]} />
<meshPhysicalMaterial
blending={THREE.NoBlending}
opacity={0}
color={"black"}
side={THREE.DoubleSide}
/>
</mesh>
</group>
</primitive>
<mesh geometry={geometry}>
<meshBasicMaterial map={bakedTexture} />
</mesh>
</>
);
}
This is the main css file:
#root {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: white;
-webkit-overflow-scrolling: touch;
}
.htmlScreen iframe {
height: 700px;
border: none;
background: #000;
overflow-y: scroll;
touch-action: touch;
}
canvas {
touch-action: touch;
}
EDIT: It seems to be something with iPhones, Android devices work correctly

How do I make a Material toolbar opaque on scroll and transparent at start?

I'm currently trying to familiarise myself with angular. I'm using angular material and I'm looking to make the material toolbar sticky and opaque on scroll and, transparent with toolbar text still visible when at the very top of the page. Everything I've searched for so far involved javascript or jquery. How do I go about it in angular 8 precisely?
This is my HTML & CSS respectively:
<mat-toolbar color="primary">
<a mat-button [routerLink]="['home']" >
<h1>PETER<span class="light">CONSTRUCTION</span></h1>
</a>
<span class="spacer"></span>
<a mat-button [routerLink]="['home']" routerLinkActive="active" >HOME</a>
<a mat-button [routerLink]="['about']" routerLinkActive="active">ABOUT</a>
<a mat-button [routerLink]="['contact']" routerLinkActive="active">CONTACT</a>
</mat-toolbar>
mat-toolbar {
position: absolute;
z-index: 1;
overflow-x: auto;
background-color: #c3cfd2;
}
mat-toolbar-row {
justify-content:space-between;
}
.spacer {
flex: 1 1 auto;
}
a.active {
background-color: rgba(0,0,0, 0.3);
}
h1 {
margin: 0;
color: black;
}
h1 .light {
font-weight: 100;
}
/*.x-bar.x-bar-absolute{*/
/* background-color: hsla(276, 6%, 63%, 0.15) !important;*/
/* transition: none !important;*/
/*}*/
/*.x-bar.x-bar-fixed{*/
/* background-color: hsla(276, 6%, 63%, 1) !important;*/
/*}*/
/*.x-bar [class^="x-bg"] {*/
/* background-color: transparent !important;*/
/*}*/
/*.x-bar.x-bar-absolute .hm5.x-menu > li > .x-anchor .x-anchor-text-primary {*/
/* color: #fff;*/
/*}*/
/*.x-bar.x-bar-fixed .hm5.x-menu > li > .x-anchor .x-anchor-text-primary {*/
/* color: #000;*/
/*}*/
There are multiple ways of achieving this, but since you're already using #angular/material, you can take advantage of the #angular/cdk and it's ScrollDispatchModule (see docs).
It allows you for easy and clean observing of scroll events for registered elements, outside of the NgZone, meaning it will have small impact on the performance.
See the example stackblitz:
https://stackblitz.com/edit/angular-npdbtp
First, you need to import ScrollDispatchModule and register provider for ScrollDispatcher:
import {ScrollDispatchModule, ScrollDispatcher} from '#angular/cdk/scrolling';
#NgModule({
imports: [
(other imports)
ScrollDispatchModule
],
providers: [ScrollDispatcher]
})
export class AppModule {}
Then in your template you can mark an html element with the cdkScrollable directive. This will automatically register it in the ScrollDispatcher.
You can also bind component's style (e.g. opacity) to a property defined in your component:
<div class="scroll-wrapper" cdkScrollable>
<mat-toolbar class="sticky-toolbar" [style.opacity]="opacity">My App</mat-toolbar>
<div>content</div>
</div>
You can make html element sticky using the display: sticky together with top: 0:
.sticky-toolbar {
position: sticky;
top: 0px;
}
Then you will need to inject the ScrollDispatcher and NgZone into your component and define opacity property:
opacity = 1;
constructor(
private scrollDispatcher: ScrollDispatcher,
private zone: NgZone
) {}
Then you can subscribe to scrolled events of the ScrollDispatcher. Those are emitted for all the registered components. You can also register to scroll events of a single element - refer to the docs if needed by.
ngOnInit(): void {
this.scrollDispatcher.scrolled().subscribe((event: CdkScrollable) => {
const scroll = event.measureScrollOffset("top");
let newOpacity = this.opacity;
if (scroll > 0) {
newOpacity = 0.75;
} else {
newOpacity = 1;
}
if (newOpacity !== this.opacity) {
this.zone.run(() => {
this.opacity = newOpacity;
});
}
});
}
The ScrollDispatcher runs outside of NgZone, meaning it will not run change detection in the whole application. This allows for better performance, and it's why we're also injecting NgZone and running the property change inside the zone - this calls the proper change detection along the tree of components.

Vue lazyloading, remove spinner when image loads

I have a vue Nuxt project where I explore lazyloading with lazysizes package.
I created a spinner component (html css only) who should be visible only while the image is loading.
I also created an ImageItem component who includes the spinner component and it looks like this:
< script >
import spinner from '~/components/spinner.vue'
export default {
components: {
spinner,
},
props: {
source: {
type: String,
required: true,
}
},
} <
/script>
<style lang="scss" scoped>.imageItem {
position: relative;
.image {
z-index: 2;
&.lazyload,
&.lazyloading {
opacity: 0;
}
&.lazyloaded {
opacity: 1;
transition: all 1s linear 0.35s;
}
}
}
</style>
<template>
<div class="imageItem">
<spinner />
<img class="image lazyload" :data-srcset="source" />
</div>
</template>
To explain my code, I have props: source where In parent component i pass the image i want to lazyload. Also in the CSS while the image is loading, the image has .lazyloading class and when is loaded .lazyloaded class. Right now when Image is loaded i put it on top of the spinner.
My problem is, when I load the image I want to hide or destroy the spinner element since I think just putting the image on top is not the best way to do it. Can someone give me direction how should I properly hide the spinner when the image is loaded ?
Lazysizes fires an event when loading the image is finished : lazyloaded event, So you can do this :
<template>
<div class="imageItem">
<spinner v-if="lazyloading"/>
<img class="image lazyload" :data-srcset="source" />
</div>
</template>
<script>
import spinner from '~/components/spinner.vue'
export default {
data(){
return {
lazyloading
}
},
mounted(){
document.addEventListener('lazyloaded', (e) => {
this.lazyloading = false;
}
});
}
}
</script>

How to make ReactPlayer scale with height and width

I am using reactplayer for a youtube video which uses iframe. I am trying to scale the video to my div and I want it to be responsive.
I put a width and height at 100% on the ReactPlayer, and I have a wrapper div that I put height and width on, but the reactplayer does not fit the div. It is stuck at a height of 150px no matter how I resize the screen.
<div className="video-wrapper>
<ReactPlayer
width="100%"
height="100%"
url="https://youtu.be/BGhqlJnFIXU"
controls
muted
config={{
youtube: {
playerVars: { showinfo: 1 }
}
}}
/>
</div>
.css
.video-wrapper {
height: 100%;
width: 100%;
min-height: 225px;
}
This can be easily achieved by further-extending your CSS. Since most videos are shot in 16:9, following this guide by Chris Coyier will make the process easily achievable.
Since you're utilizing React-Player, I am working with the content located on their demo page.
.player-wrapper {
width: auto; // Reset width
height: auto; // Reset height
}
.react-player {
padding-top: 56.25%; // Percentage ratio for 16:9
position: relative; // Set to relative
}
.react-player > div {
position: absolute; // Scaling will occur since parent is relative now
}
Why it works?
TL;DR - Padding in percentages is based on width. By setting an element's height to 0, we can utilize a percentage for 'padding-top' to scale content perfectly.
Generate 16:9's Percentage
(9 / 16) * 100 = 56.25
To force react-player be fully responsive I did the following:
CSS
.player-wrapper {
position: relative;
padding-top: 56.25%; /* 720 / 1280 = 0.5625 */
}
.react-player {
position: absolute;
top: 0;
left: 0;
}
JSX
import React from "react";
import ReactPlayer from "react-player";
import "./Player.css";
const Player = () => (
<div className="player-wrapper">
<ReactPlayer
url="https://youtu.be/3eLrINg3O2Q"
className="react-player"
playing
width="100%"
height="100%"
controls={false}
/>
</div>
);
export default Player;
The Easiest way to make the it responsive is adding the widht as 100%
<ReactPlayer
controls
pip
width="100%"
url={_post.videolink}
/>
you can fill with:
.react-player > video {
position: absolute;
object-fit: fill;
}
For a more modern approach, just add a class to the react player component and set it to:
height: auto !important;
aspect-ratio: 16/9;
https://caniuse.com/mdn-css_properties_aspect-ratio
you can keep the width fixed and then allow the height to adjust according to the video height as different video has its own size like 100*200.
.video-wrapper > video { width: 55vw; height: min-content; }
Here's how I did it.
video { object-fit: cover; }
Now, the size of the video can be adjusted by sizing the wrapper.
If you are using Tailwindcss and Swiper you can use this code
This code implements the last aspect-video class to ensure the aspect ratio always be correct
And also ensure that if the user scroll to the next page, the previous player gets paused
You can also set Max width of videos , here its "max-w-6xl"
import React, { useEffect, useState } from 'react'
import ReactPlayer from 'react-player'
import { Swiper, SwiperSlide } from 'swiper/react';
import { Pagination } from "swiper";
const videos = [
{
id: 1,
url: "https://www.youtube.com/watch?v=1234"
},
{
id: 2,
url: "https://www.youtube.com/watch?v=1234"
},
{
id: 3,
url: "https://www.youtube.com/watch?v=1234"
}
];
const IndexHero = () => {
const [domLoaded, setDomLoaded] = useState(false);
const [isPlaying, setIsPlaying] = useState(null);
useEffect(() => {
setDomLoaded(true);
}, []);
return (
<>
<div className='h-auto'>
{!domLoaded && (
<div className="flex items-start justify-center">
<div className="flex-1 max-w-6xl">
<div className="aspect-video">
{/** For CLS */}
</div>
</div>
</div>
)}
{domLoaded && (
<Swiper
modules={[Pagination]}
pagination={true}
className="h-full "
onSlideChange={() => {
setIsPlaying(null);
}}
autoplay={false}
watchSlidesProgress={true}
>
{videos.map((data) => (
<SwiperSlide key={data.id}>
<div className="flex items-start justify-center">
<div className="flex-1 max-w-6xl">
<div className="aspect-video">
<ReactPlayer
key={data.id}
url={data.url}
width="100%"
height="100%"
controls={true}
onPlay={() => {
setIsPlaying(data.id);
}}
playing={isPlaying === data.id}
/>
</div>
</div>
</div>
</SwiperSlide>
))}
</Swiper> )}
</div>
</>
);
};
export default IndexHero;

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