Creating a parallax affect with react-scroll-parallax and image masks - css

Here is the desired outcome I'm looking to achieve by scrolling using react-scroll-parallax.
On Mobile browser
View web browser example here
Description
I want to create a website with the parallax affect shown above. The key elements being a website build in react containing three pages.
While scrolling from Page 1 to Page 2 I want the mobile device mock to start halfway on the screen (as to avoid the other content of page 1), then move to being basically centered.
While scrolling from Page 2 to Page 3, the website and components stick and once again act like a normal website scroll.
Additionally, during the scroll from Page 1 to Page 2, I want the content inside the device mock to scroll as well.
What I tried
For starters I was able to get nearly the affect I wanted by using a div with it's z-index and absolute position set, and parallax on translateY of -50, 125.
<div className={"absolute z-10 w-full"}>
<Parallax translateY={[-50, 125]}></Parallax>
</div>
The problem became however when I wanted to place content inside the div. Having another div within the parallax that also had z-index set seemed to mess with the parallax affect.
Important notes
Content inside device mock
One issue I found that was tricky was trying to place the content inside the device mock. I want a parallax both on the device mock itself, and the content within it.
I'm not entirely sure how I should crop the content inside the device mock.
The device mock svg frame and device mock mask can be found here if you want to give it a try
Device mock svg and mask
I tried imgs with various z-indexes, masking the div with an svg mask, using image backgrounds. Nothing is quite getting the preferred outcome.
Scaling of device mock
I want to make sure this works well on both mobile and browser. With that said I was trying to use margins to scale the device mock but I had a hard time with trying to then correctly get the mask to work for the content within the mock.
I'm not sure if using dedicated width and height sizes would be the ideal way to go, but very open to suggestions! It seems hard to scale the device frame and the mask properly.
Parallax of device and parallax of device content
I want the content inside the device mock to be html so that I can change it more than just an image. That being said the most important feature I want is for both the device and the content inside to have a parallax scroll affect.
Summary
I know this is a bit much for a quick simple stack overflow issue, but I've been trying a lot to get this to work and just can't seem to nail down the little details correctly. I sincerely appreciate all help and suggestions and if there is anything else I can provide please let me know!

The trickier part of the request was blowing up the <svg>, adding new <path /> and <clipPath /> for the color swap inside the phone mock.
Eventually I got it working here. The part linking the clipPath transition to the scroll progress looks like this:
const [y, setY] = React.useState(1739);
const onProgressChange = React.useCallback(
(a) =>
setY(Math.max(Math.min(1739, 1739 - ((a - 0.24) / 0.0018) * 17), 36)),
[setY]
);
const { ref } = useParallax({
translateY: [0, 185],
onProgressChange
});
The 1739 and 36 are max and min values for the translation and they are strictly related to the svg's viewBox. The other values allow tweaking the start, end and speed of animation, with regards to overall scroll progress.
This, together with some CSS, took care of binding the right animations to the correct scroll progress.
Feel free to tweak it some more, especially if you add more markup.
The other thing I wanted was a function activated shortly after scrolling, which would snap the scroll to certain positions. Namely, to the .page elements.
I used gsap's ScrollTrigger plugin for the task, for multiple reasons:
I'm somewhat familiar with it (used it before)
it's performant, light and non-obtrusive (basically quits when it detects another user scroll)
listens to all relevant events (touch, mouse pointer, keyboard) without me having to make sense of them, providing a unified interface.
uses inertia (if you scroll down faster from page 1 it will scroll past page 2, directly to page 3 - other scroll plugins limit you to having to scroll once for each page change)
works well on mobile devices
There are other libs/plugins out there for the task, you don't have to use gsap (although I do think it's awesome). If you end up including it in your project, make sure you comply with their licensing (which is not MIT).
By the way, my first choice for the parallax effect per-se would also be gsap, as their timelines provide a lot of flexibility and options.
Their most advanced stuff is reserved for subscribers, but even if you limit yourself to the free plugins, you're still getting more than from alternative libs/plugins, IMHO.
See it working.

Related

Interactive SVG in WordPress

I've designed and coded an interactive SVG that I've had to implement as HTML in WordPress because it only acts as a useless static image if it's entered as an SVG in an image block.
So far, that means that it's not responsive and loads at full size on a phone. I want people to see it in full, straight away, not zoom out to see it.
Is there another way to make it work without being an HTML dump? And even as code, how would I reduce it to fit screen sizes? Bearing in mind that my brain may implode if you suggest coding breakpoints or something like that.
I'm using a Blocksy child theme with no page builder.
The code itself works fine so there seems no point in me pasting a shortened version of the code. The page is here, if that helps.
Www.orderaround.co.uk
Right, I've fixed it myself. All I needed to do was remove the width and height specifications from the beginning of the svg code. It now fits to whatever container it's being displayed in.

Manually invalidating a composite layer in Chrome

Background
I'm working on a web app built in HTML (not WebGL/Canvas) which includes 2d viewport controls for panning and zooming the page content. Something like Figma, perhaps, but rendered entirely with DOM, which is a hard technical requirement.
To achieve the viewport functionality I've made extensive use of CSS transform to power all offsets and animations in order to reduce the work required to render changes to compositing, as much as possible. The "canvas" of my app contains many discrete items which can be moved and resized by the user, similar to any typical OS window manager. These 'widgets' may contain their own scrollable content.
For example, after panning 50px,25px and zooming to 1.5x, the DOM and transform values might look like this for a particular "canvas" which has a widget at (20, 100):
<div id="canvas" style="transform: scale(1.5, 1.5) translate(50px, 25px)">
<div id="widget-1" style="transform: translate(20px, 100px)" />
</div>
After a lot of experimentation I've discovered that the most efficient way to render these items across multiple browsers is to promote each individual 'widget' to its own layer by applying will-change: transform to the outermost element. This results in a pretty reasonable framerate, even with a lot of content in the frame while panning and zooming.
Webkit Misbehavior
However, there's one catch - on Webkit-based browsers, when zooming (which is applied via scale transform in CSS on the root canvas element), the contents of the widgets are not re-rasterized to accommodate the new scale value. At a zoom greater than 1x, this produces noticeable blurriness. Images with text, in particular, are basically unreadable.
Above, one image widget and another DOM text widget at 1x (native) scale.
And now, at 2x scale (you won't be able to tell the difference inline in this post, but you can see it at full resolution). Notice that the image is just as illegible as before, and the text is blurry.
For a live reproduction of this problem, see this CodeSandbox (leave "Animation" unchecked).
Side note: this only happens on Chrome, Safari, and Edge - so it seems like an artifact of Webkit's rendering behavior. Firefox actually scales everything quite nicely, and with a faster framerate to boot.
However, the performance of this approach is desirable. After trying some other configurations of layering, I decided the best approach would be to try to force the browser to re-rasterize the widgets once a zoom change animation was completed.
The Hack
The intended goal is to allow the old rasterized textures to persist during the zoom animation to make it as smooth as possible (so the blurriness seen above will be present while the viewport scales up/down), but to trigger a re-rasterization at the final scale once the animation is complete - re-draw all the widgets, taking current scale into account so that their contents are sharp and legible even at 2x zoom.
To correct this problem, I have implemented what feels like a "hack": after the end of each zoom animation, I'm toggling the will-change on all the widgets from transform to initial for 1 frame, and then back again:
const rerasterize = () => {
requestAnimationFrame(() => {
element.style.willChange = 'initial';
requestAnimationFrame(() => {
element.style.willChange = 'transform';
});
});
};
This actually works. Now when I zoom in to 2x, the image text is legible and the DOM text is nice and sharp:
(In my app, you can even see the moment when the zoom animation "settles" and text "pops" into high-resolution.)
However, if I understand correctly, the code above is actually forcing the browser to dispose of the composite layer for each widget for 1 frame, and then recreate it. While the performance seems acceptable in practice, I would much prefer to just ask the browser to invalidate the layer which it has already constructed.
The Question
So, with all that context aside, the question is simply: is there a way to manually trigger an invalidation of a composite layer without trashing it? A magic CSS incantation, perhaps?
I'm also open to alternative approaches with respect to layer grouping which might improve behavior without harming render performance.
Other Stuff I Tried
One thing I noticed when creating the reproduction CodeSandbox is that if I add a transition property to the "canvas" element (which is being transformed to achieve the viewport changes), even if widgets are composited in different layers, it appears to fix the blurriness. You can see this by checking "Animation" in the demo. However, my animations are currently done via JS, so adding a secondary CSS transition on top of this doesn't seem like a great plan.
I tried ripping out JS animations entirely and relying solely on transition, but surprisingly this did not seem to help. Panning and zooming felt noticeably choppier (some of this might come down to native easing feeling less natural than JS spring-based easing), but more concerningly the GPU memory usage and dropped frames were notably worse than without transition - which leads me to believe that transition might be causing a lot more work than I really want on the GPU for my use case (perhaps invalidating layers frequently during animations, when I would prefer them to remain intact until the transition ends).

Responsive thumbnails - shrink up to a point and restore hiding one

I have been trying to code a responsive thumbnail group that I'd like it to respect to a few rules.
I came very close but the implementation always end up not satisfying at least one of the rules. In other words when I try to do one thing it always cancels the other.
In summary, what I am trying to achieve is something like this -
http://postimg.org/image/4yx6poscz/f57d6517/ (I wasn't allowed to post a picture, sorry)
So basically, what I am after is;
When the user resizes the window the thumbnails (consist of an image and some basic HTML) will shrink up to a point.
When the breakpoint is reached, the most left one will disappear and the rest of the thumbnails will return to their original size.
Continue until the min-body size where we just display the x-axis scrollbar.
So, referring to the image, providing that no resizing is done just yet, the items will shrink, lets say until 20% and then Item 7 will disappear letting free space to the rest of the row to expand to their original size.
I am currently using the latest version of bootstrap however, as you may guess col-lg col-md and etc. classes are not really helpful in this case. I have come up with some custom breakpoints but I can't get my head around the shrinking back to the original size issue. That is where I get stuck.
Also I think it's worth mentioning that Ive been trying to come up with a solution that is pure HTML and CSS however I am definitely open to JS solutions as well.
A good example would be spotify's webplayer (play.spotify.com) thumbnail examples.
Thank you very much in advance for your suggestions.

How to fix Responsive form layout

I am building this form: http://codepen.io/anon/pen/gFoIG/
and so far I am satisfied. Unfortunately I have some issue that I seem not able to fix.
First of all, I want to enforce the label positions beside their respective inputs. Now the form breaks down easily (with the opera emulator for smartphone and tablet, the privacy label goes below the check, but I want that it stays beside it)
The other issue that I have is that I want it to fall back gracefully when there are small screens, like placing the second image below the first and placing the input controls one below the other and take the full screen width to be bigger and easier to interact with, but so far I only was able to break the layout with my tests.
This issue: the the form layout breaks, the internal control (input, button, etc) go outside the container div. How do I enforce the container to keep everything inside? I've experimented with blocks, floats and whatever, but if the layout breaks, the input boxes usually go outside the gray rectangle.
Last issue: If I insert this form inside an existing website (for example, a page in wordpress) the layout get completely destroyed because influences from the theme style. How do I enforce my style on my form, keeping it isolated from the other styles? I can think of the iframe as a solution, but it is the only one? It is a good practice?
Anyone can help me with that?
You might want to take a look here. Its a site I just set up to explain an approach to responsive using a jQuery plugin to manage redoing layout. I think it could work for your example quite easily. Also because it can target a container div at any depth in a web page, it could be helpful in the scenario where the layout you want to reflow is inside a 3rd party container (as long as you can run script on the page).

web app CSS trouble

I'm trying to present my notecards in a web app style.
I'm not worried about caching, or making it work offline.
I just want it render well in the iOS browser.
Here's the link: http://kaninepete.com/flashcard/review.php?Sec=3
I want it to look the same as if you re-size your browser window to 320x480.
The problem is, it always renders a huge amount of blank space off to the side.
I want to lock the scrolling to only the vertical axis (like flipping through notecards),
but also have the text at a readable size.
You can use CSS media queries to set your template on a certain width/height model. This works well and can adjust specifically for iPhone screens.
As for the font size issue you'll probably need to just spend time testing. With that it's going to require some type of virtual simulator or a real iPhone where you can test the site. I just loaded it up onto my iPhone 4 and I see what you mean about additional space - this is just because of your page size. Try messing with CSS media queries I think you'll find the answer in there.
Here is a very handy Google search to hopefully get you started on the right track. CSS3 has a lot of new features. Many of them geared towards mobile :)
Reading your question again, here's some suggestions based on what I think you're looking for.
Make sure your document is valid HTML before you continue. Safari on iOS supports HTML 5, so I'd suggest targeting that, unless your platform targets something different already.
If you just want it to run well in iOS Safari, then code for that. If you want it to look similarly in other browsers, however, then it may be necessary to look at styles targeting the iOS device (via width/height). See http://davidbcalhoun.com/2010/using-mobile-specific-html-css-javascript (It seems hacky, but based on some research a week ago, this still seems to be the suggested route.)
You've got CSS that shouldn't be in there if you want to target multiple browsers. overflow:hidden and set pixel widths.
Generally, I'd say you'll want to tweak your markup as well. List items or headers would be much better than just simple breaks.
Maybe I'm just oversimplifying the question, but it looks to me like all you really need to do is wrap each notecard in a div, perhaps giving each div a <div class="notecard_wrapper">. then just attach a stylesheet that specifies the width and height you want for each card.
This page explains Safari's viewport and how to change it. It will probably fix the font size problem and maybe help with the page size.
Basically, Safari by default simulates a screen that's about 900px wide, when it's actually about 300px (so the page appears zoomed out). This makes pages designed for real computers render properly, but for a web app you usually don't want it to zoom the page at all. The viewport tag should let you control that.

Resources