I've been building this page using scroll-snap and it's working well on Firefox desktop & mobile, as well as Chrome mobile.
But on Chrome desktop (Edge desktop as well) I get very choppy/laggy scroll animation, to the point I don't consider it usable as it is.
I've been looking for a few hours now, to no avail. Any help or lead or idea would be greatly appreciated.
I'm on Windows 10.
Here's the SCSS for the snap container and items:
.works {
margin: 0;
padding: 0;
scroll-snap-type: y mandatory;
scroll-behavior: smooth;
overflow: scroll;
height: calc(100vh - var(--header-height) - 2vh);
width: 100vw;
position: absolute;
top: calc(var(--header-height) + 2vh);
> div {
margin: 0 auto;
padding: 2vh 0 0;
display: block;
width: clamp(200px, 100vw, 1280px);
min-height: 100%;
z-index: -1;
background-origin: border-box;
background-repeat: no-repeat;
background-attachment: fixed;
background-position: center;
background-size: auto 100%;
border-radius: .5em;
border: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
display: grid;
grid-template-rows: auto 7rem auto;
scroll-snap-align: start;
scroll-snap-stop: always;
}
}
And the matching React component:
function Works() {
const contents = worksContents.map( ({ title, year, text, pic }, i) => {
const styles = {
backgroundImage: `linear-gradient(transparent, #fdfdfd 75%), url(${pic})`,
}
return (
<div key={ i } style={ styles }>
<span className='title'> { title } </span>
<span className='year'> { year } </span>
{ text }
</div> )
}
)
return (
<section className="works">
{ contents }
</section>
)
}
export default Works
I just wanted to second this question as its affecting me too. Scroll-Snap seems to be completely unusable in desktop chrome at the moment.
I made a codepen to demonstrate the problem:
https://codepen.io/lumakker/pen/poaybJd
html:
<div class="testdiv" style="background-color:white">
<p>compare between scrolling with mousewheel and scrolling with autoscroll (middle mouse button click). Mousewheell scroll-snap is very choppy on chrome desktop.</p>
</div>
<div class="testdiv" style="background-color:black"></div>
<div class="testdiv" style="background-color:green"></div>
<div class="testdiv" style="background-color:red"></div>
css:
html {
scroll-snap-type: y mandatory;
height: 100%
}
body {
height: 100%
}
.testdiv {
height: 100%;
scroll-snap-align: start;
}
I hope someone has a solution because I would really like to avoid using a custom scroll-snap js for my website.
Related
I'm looking for a CSS solution that adapts to div contents, with the functionality of clip-path but dynamic. This is my code:
.background {
background: yellow;
text-align: center;
}
.text {
display: inline-block;
margin: 20px;
padding: 20px;
background: teal;
}
<div class="background">
<div class="text">
My text is in here
</div>
</div>
Yellow and teal are just used for illustration. I want to replace the yellow background with an image, but only show it in the teal area. The div.background spans the width of the browser, but I cannot make assumptions about the width of div.text. Can this be done with only CSS or does it require JS and dynamically setting background-position?
Use a pseudo element that you make relative to the background element
.background {
background: yellow;
text-align: center;
position: relative;
z-index: 0;
}
.text {
display: inline-block;
color: #fff;
margin: 20px;
padding: 20px;
clip-path: inset(0); /* clip to only text element */
}
.text:before {
content: "";
position: absolute;
z-index: -1;
inset: 0;
background: url(https://picsum.photos/id/1056/800/600) center/cover;
}
/* to illustrate */
.text:hover {
clip-path: none;
}
<div class="background">
<div class="text">
My text is in here
</div>
</div>
Here is one way of doing what you want through JS. The image is in the background element, and it is clipped according to the dimensions of the child element. There's a resize observer applied to the child element to trigger the calculation of the clipping mask whenever the dimensions of the child change.
I've added an animation to show how the clipping is calculated in real-time, but as you can see there is some slight stutter.
let text = document.querySelector('.text');
let bg = document.querySelector('.background');
let observer = new ResizeObserver(() => {
calculateClipPath(bg, text);
})
observer.observe(text);
function calculateClipPath (parent, child) {
parent.style.clipPath = `inset(
${child.offsetTop}px
${parent.clientWidth - (child.offsetLeft + child.clientWidth)}px
${parent.clientHeight - (child.offsetTop + child.clientHeight)}px
${child.offsetLeft}px
)`;
}
.background {
background: url(https://c4.wallpaperflare.com/wallpaper/368/148/1024/flowers-roses-drawing-light-wallpaper-preview.jpg);
text-align: center;
position: relative;
}
.text {
display: inline-block;
margin: 20px;
padding: 40px;
width: 200px;
animation: 3s infinite change;
}
#keyframes change {
0% {
width: 200px;
}
50% {
width: 150px;
}
100% {
width: 200px;
}
}
<div class="background">
<div class="text">
My text is in here
</div>
</div>
I'm still experimenting to see if there is a purely CSS version of the solution because that would always be smoother than the JS solution. If I can figure it out, I'll edit this answer and add it here
I've added inline-inset-end and inset-inline-start to an absolutely positioned element. I'm using them for language support as these two should help when the layout needs to switch between RTL and LTR.
This is working fine on chrome and forefox but I have absolutely no idea how to set up an equivalent in Safari.
I'm trying to understand if there is a basic css alternative that will work on Safari
An example of what the layout would be like:
.container {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
background: blue;
}
.reader {
width: 50px;
height: 100%;
display : flex;
flex-direction : column;
position: absolute;
inset-inline-end: 0px;
background: yellow;
}
<html>
<body>
<div class="container">
ha ha
<div class="reader">
hola
</div>
</div>
</body>
</html>
Safari supports inset-inline-end and inset-inline-start now (and most other logical properties), but if you still need a fallback there are options. As long as you are using the dir attribute properly, you can add a fallback that works in basically any browser.
[dir="ltr"] .reader {
right: 0px;
}
[dir="rtl"] .reader {
left: 0px;
}
If you want to go a step further (at the cost of some support), you can use #supports:
#supports not (inset-inline-end: 0px) {
[dir="ltr"] .reader {
right: 0px;
}
[dir="rtl"] .reader {
left: 0px;
}
}
The same trick can be used with most logical properties as well.
I am very new to css. I have a flow of pictures which I preload in js to determine if they are portraits or landscapes. Then I want to return a div according to that. My css looks like that
.image-wrapper-landscape {
width: 100%;
height: 100%;
display: inline-block;
position: relative;
margin: 5px 0;
&:hover {
.text-wrapper {
opacity: 1;
}
}
}
.image-wrapper-portrait {
width : 49.5%;
display: inline-block;
position: relative;
margin: 5px 0;
&:hover {
.text-wrapper {
opacity: 1;
}
}
}
Then my divs are
<div
className='image-wrapper-portrait'
key={i}
>
<img src={image.src} alt={altText} data-position={image.position} />
</div>
<div
className='image-wrapper-landscape'
key={i}
>
<img src={image.src} alt={altText} data-position={image.position} />
</div>
This doesn't seem to work though since the portrait pictures all look different. What I would like to do is resize all of them so that I end up with a flow of either landscapes or portraits, I also tried pixels instead of percentages but still nothing. I would appreciate nay help, css confuses me.
I think it's working for you, try making the img width 100%;
But while using scss try to make it useful.
.scss
.image-wrapper {
display: inline-block;
position: relative;
margin: 5px 0;
&.portrait {
width: 100%;
height: 100%;
}
&.landscape {
width : 49.5%;
}
&:hover {
.text-wrapper {
opacity: 1;
}
}
img {
width: 100%;
}
}
use class name image-wrapper portrait for portrait and use image-wrapper landscape.
And use object-fit if you wants to.
On my page, the top center embedded YouTube video (iframe) fills the entire responsive container on Firefox, but for some reason it is displayed much smaller and only in the upper half of the container on Chrome:
HTML:
<div class='product_frame_wrap'>
<div class='product_frame'>
<div class='product_frame_image_vid'>
<iframe>...</iframe>
</div>
</div>
</div>
CSS:
.product_frame_wrap {
margin: 0 auto;
max-width: 600px;
position: relative;
width: 100%;
}
.product_frame {
background: rgba(0, 0, 0, 0) url("images/frame.png") no-repeat scroll 50% 50% / contain ;
padding-bottom: 75%;
position: relative;
}
.product_frame_image_vid {
bottom: 0;
left: 0;
padding-top: 5%;
position: absolute;
right: 0;
top: 0;
}
.product_frame_image_vid iframe {
height: 100% !important;
width: 100% !important;
}
If you look up browser specific css you can find hacks. The below code will only effect chrome browsers. When you set the iframe's height to 200% it seems to fill in correctly in chrome.
Adding the below snippet to your css should fix the problem:
#media screen and (-webkit-min-device-pixel-ratio:0) {
.product_frame_image_vid iframe { height:200%; }
}
The scrollbar on my iframe will not stay visible. It appears when the iframe first loads, then fades away.
I have tried:
putting scrolling="yes" in iframe tag
adding overflow: -moz-scrollbars-vertical in css
adding overflow-y:scroll in css
None of these work.
#iframe {
overflow: -moz-scrollbars-vertical !important;
overflow-y:scroll;
}
<iframe class="iframeclass" id="frame" src="" width="650" height="350" frameBorder="0" scrolling="yes"></iframe>
I think this is a Mac issue, since many Mac users disable the vertical scroll bar since they just use their trackpad. I tried the following (from this answer):
.iframeclass::-webkit-scrollbar {
-webkit-appearance: none;
}
.iframeclass::-webkit-scrollbar:vertical {
width: 11px;
}
.iframeclass::-webkit-scrollbar:horizontal {
height: 11px;
}
.iframeclass::-webkit-scrollbar-thumb {
border-radius: 8px;
border: 2px solid white; /* should match background, can't be transparent */
background-color: rgba(0, 0, 0, .5);
}
.iframeclass::-webkit-scrollbar-track {
background-color: #fff;
border-radius: 8px;
}
But still does not work.
Check this:
<div class="scroll-wrapper">
<iframe src=""></iframe>
.scroll-wrapper {
-webkit-overflow-scrolling: touch;
overflow-y: scroll;
/* important: dimensions or positioning here! */
}
.scroll-wrapper iframe {
/* nada! */
}
.demo-iframe-holder {
position: fixed;
right: 0;
bottom: 0;
left: 0;
top: 0;
-webkit-overflow-scrolling: touch;
overflow-y: scroll;
}
.demo-iframe-holder iframe {
height: 100%;
width: 100%;
}
This supports all browsers.
Check Article for more info: Article