Tag: Transition

How to Transition to Manifest V3 for Chrome Extensions

While I am not a regular Chrome extension programmer, I have certainly coded enough extensions and have a wide enough web development portfolio to know my way around the task. However, just recently, I had a client reject one of my extensions as I received feedback that my extension was “outdated”.

As I was scrambling to figure out what was wrong, I swept my embarrassment under the carpet and immediately began my deep dive back into the world of Chrome Extensions. Unfortunately, information on Manifest V3 was scarce and it was difficult for me to understand quickly what this transition was all about.

Needless to say, with a pending job, I had to painstakingly navigate my way around Google’s Chrome Developer Documentation and figure things out for myself. While I got the job done, I did not want my knowledge and research in this area to go to waste and decided to share what I wish I could have had easy access to in my learning journey.

Why the transition to Manifest 3 is important

Manifest V3 is an API that Google will use in its Chrome browser. It is the successor to the current API, Manifest V2, and governs how Chrome extensions interact with the browser. Manifest V3 introduces significant changes to the rules for extensions, some of which will be the new mainstay from V2 we were used to.

The transition to Manifest V3 can be summarized as such:

  1. The transition has been ongoing since 2018.
  2. Manifest V3 will officially begin rolling out in January 2023.
  3. By June 2023, extensions that run Manifest V2 will no longer be available on the Chrome Web Store.
  4. Extensions that do not comply with the new rules introduced in Manifest V3 will eventually be removed from the Chrome Web Store.

One of the main goals of Manifest V3 is to make users safer and improve the overall browser experience. Previously, many browser extensions relied on code in the cloud, meaning it could be difficult to assess whether an extension was risky. Manifest V3 aims to address this by requiring extensions to contain all the code they will run, allowing Google to scan them and detect potential risks. It also forces extensions to request permission from Google for the changes they can implement on the browser.

Staying up-to-date with Google’s transition to Manifest V3 is important because it introduces new rules for extensions that aim to improve user safety and the overall browser experience, and extensions that do not comply with these rules will eventually be removed from the Chrome Web Store.

In short, all of your hard work in creating extensions that used Manifest V2 could be for naught if you do not make this transition in the coming months.

January 2023 June 2023 January 2024
Support for Manifest V2 extensions will be turned off in Chrome’s Canary, Dev, and Beta channels. The Chrome Web Store will no longer allow Manifest V2 extensions to be published with visibility set to Public. The Chrome Web Store will remove all remaining Manifest V2 extensions.
Manifest V3 will be required for the Featured badge in the Chrome Web Store. Existing Manifest V2 extensions that are published and publically visible will become unlisted. Support for Manifest 2 will end for all of Chrome’s channels, including the Stable channel, unless the Enterprise channel is extended.

The key differences between Manifest V2 and V3

There are many differences between the two, and while I highly recommend that you read up on Chrome’s “Migrating to Manifest V3” guide, here is a short and sweet summary of key points:

  1. Service workers replace background pages in Manifest V3.
  2. Network request modification is handled with the new declarativeNetRequest API in Manifest V3.
  3. In Manifest V3, extensions can only execute JavaScript that is included within their package and cannot use remotely-hosted code.
  4. Manifest V3 introduces promise support to many methods, though callbacks are still supported as an alternative.
  5. Host permissions in Manifest V3 are a separate element and must be specified in the "host_permissions" field.
  6. The content security policy in Manifest V3 is an object with members representing alternative content security policy (CSP) contexts, rather than a string as it was in Manifest V2.

In a simple Chrome Extension’s Manifest that alters a webpage’s background, that might look like this:

// Manifest V2 {   "manifest_version": 2,   "name": "Shane's Extension",   "version": "1.0",   "description": "A simple extension that changes the background of a webpage to Shane's face.",   "background": {     "scripts": ["background.js"],     "persistent": true   },   "browser_action": {     "default_popup": "popup.html"   },   "permissions": [ "activeTab", ],   "optional_permissions": ["<all_urls>"] }
// Manifest V3 {   "manifest_version": 3,   "name": "Shane's Extension",   "version": "1.0",   "description": "A simple extension that changes the background of a webpage to Shane's face.",   "background": {     "service_worker": "background.js"   },   "action": {     "default_popup": "popup.html"   },   "permissions": [ "activeTab", ],   "host_permissions": [ "<all_urls>" ] }

If you find some of the tags above seem foreign to you, keep reading to find out exactly what you need to know.

How to smoothly transition to Manifest V3

I have summarized the transition to Manifest V3 in four key areas. Of course, while there are many bells and whistles in the new Manifest V3 that need to be implemented from the old Manifest V2, implementing changes in these four areas will get your Chrome Extension well on the right track for the eventual transition.

The four key areas are:

  1. Updating your Manifest’s basic structure.
  2. Modify your host permissions.
  3. Update the content security policy.
  4. Modify your network request handling.

With these four areas, your Manifest’s fundamentals will be ready for the transition to Manifest V3. Let’s look at each of these key aspects in detail and see how we can work towards future-proofing your Chrome Extension from this transition.

Updating your Manifest’s basic structure

Updating your manifest’s basic structure is the first step in transitioning to Manifest V3. The most important change you will need to make is changing the value of the "manifest_version" element to 3, which determines that you are using the Manifest V3 feature set.

One of the major differences between Manifest V2 and V3 is the replacement of background pages with a single extension service worker in Manifest V3. You will need to register the service worker under the "background" field, using the "service_worker" key and specify a single JavaScript file. Even though Manifest V3 does not support multiple background scripts, you can optionally declare the service worker as an ES Module by specifying "type": "module", which allows you to import further code.

In Manifest V3, the "browser_action" and "page_action" properties are unified into a single "action" property. You will need to replace these properties with "action" in your manifest. Similarly, the "chrome.browserAction" and "chrome.pageAction" APIs are unified into a single “Action” API in Manifest V3, and you will need to migrate to this API.

// Manifest V2 "background": {   "scripts": ["background.js"],   "persistent": false }, "browser_action": {   "default_popup": "popup.html" },

// Manifest V3 "background": {   "service_worker": "background.js" }, "action": {   "default_popup": "popup.html" }

Overall, updating your manifest’s basic structure is a crucial step in the process of transitioning to Manifest V3, as it allows you to take advantage of the new features and changes introduced in this version of the API.

Modify your host permissions

The second step in transitioning to Manifest V3 is modifying your host permissions. In Manifest V2, you specify host permissions in the "permissions" field in the manifest file. In Manifest V3, host permissions are a separate element, and you should specify them in the "host_permissions" field in the manifest file.

Here is an example of how to modify your host permissions:

// Manifest V2 "permissions": [    "activeTab",    "storage",    "http://www.css-tricks.com/",    ":///*"  ]
// Manifest V3 "permissions": [    "activeTab",    "scripting",    "storage" ], "host_permissions": [   "http://www.css-tricks.com/"  ], "optional_host_permissions": [    ":///*"  ]

Update the content security policy

In order to update the CSP of your Manifest V2 extension to be compliant with Manifest V3, you will need to make some changes to your manifest file. In Manifest V2, the CSP was specified as a string in the "content_security_policy" field of the manifest.

In Manifest V3, the CSP is now an object with different members representing alternative CSP contexts. Instead of a single "content_security_policy" field, you will now have to specify separate fields for "content_security_policy.extension_pages" and "content_security_policy.sandbox", depending on the type of extension pages you are using.

You should also remove any references to external domains in the "script-src", "worker-src", "object-src", and "style-src" directives if they are present. It is important to make these updates to your CSP in order to ensure the security and stability of your extension in Manifest V3.

// Manifest V2 "content_security_policy": "script-src 'self' https://css-tricks.com; object-src 'self'"
// Manfiest V3 "content_security_policy.extension_pages": "script-src 'self' https://example.com; object-src 'self'", "content_security_policy.sandbox": "script-src 'self' https://css-tricks.com; object-src 'self'"

Modify your network request handling

The final step in transitioning to Manifest V3 is modifying your network request handling. In Manifest V2, you would have used the chrome.webRequest API to modify network requests. However, this API is replaced in Manifest V3 by the declarativeNetRequest API.

To use this new API, you will need to specify the declarativeNetRequest permission in your manifest and update your code to use the new API. One key difference between the two APIs is that the declarativeNetRequest API requires you to specify a list of predetermined addresses to block, rather than being able to block entire categories of HTTP requests as you could with the chrome.webRequest API.

It is important to make these changes in your code to ensure that your extension continues to function properly under Manifest V3. Here is an example of how you would modify your manifest to use the declarativeNetRequest API in Manifest V3:

// Manifest V2 "permissions": [   "webRequest",   "webRequestBlocking" ]

// Manifest V3 "permissions": [   "declarativeNetRequest" ]

You will also need to update your extension code to use the declarativeNetRequest API instead of the chrome.webRequest API.

Other aspects you need to check

What I have covered is just the tip of the iceberg. Of course, if I wanted to cover everything, I could be here for days and there would be no point in having Google’s Chrome Developers guides. While what I covered will have you future-proofed enough to arm your Chrome extensions in this transition, here are some other things you might want to look at to ensure your extensions are functioning at the top of their game.

  • Migrating background scripts to the service worker execution context: As mentioned earlier, Manifest V3 replaces background pages with a single extension service worker, so it may be necessary to update background scripts to adapt to the service worker execution context.
  • Unifying the **chrome.browserAction** and **chrome.pageAction** APIs: These two equivalent APIs are unified into a single API in Manifest V3, so it may be necessary to migrate to the Action API.
  • Migrating functions that expect a Manifest V2 background context: The adoption of service workers in Manifest V3 is not compatible with methods like chrome.runtime.getBackgroundPage(), chrome.extension.getBackgroundPage(), chrome.extension.getExtensionTabs(), and chrome.extension.getViews(). It may be necessary to migrate to a design that passes messages between other contexts and the background service worker.
  • Moving CORS requests in content scripts to the background service worker: It may be necessary to move CORS requests in content scripts to the background service worker in order to comply with Manifest V3.
  • Migrating away from executing external code or arbitrary strings: Manifest V3 no longer allows the execution of external logic using chrome.scripting.executeScript({code: '...'}), eval(), and new Function(). It may be necessary to move all external code (JavaScript, WebAssembly, CSS) into the extension bundle, update script and style references to load resources from the extension bundle, and use chrome.runtime.getURL() to build resource URLs at runtime.
  • Updating certain scripting and CSS methods in the Tabs API: As mentioned earlier, several methods move from the Tabs API to the Scripting API in Manifest V3. It may be necessary to update any calls to these methods to use the correct Manifest V3 API.

And many more!

Feel free to take some time to get yourself up to date on all the changes. After all, this change is inevitable and if you do not want your Manifest V2 extensions to be lost due to avoiding this transition, then spend some time arming yourself with the necessary knowledge.

On the other hand, if you are new to programming Chrome extensions and looking to get started, a great way to go about it is to dive into the world of Chrome’s Web Developer tools. I did so through a course on Linkedin Learning, which got me up to speed pretty quickly. Once you have that base knowledge, come back to this article and translate what you know to Manifest V3!

So, how will I be using the features in the new Manifest V3 going forward?

Well, to me, the transition to Manifest V3 and the removal of the chrome.webRequest API seems to be shifting extensions away from data-centric use cases (such as ad blockers) to more functional and application-based uses. I have been staying away from application development lately as it can get quite resource-intensive at times. However, this shift might be what brings me back!

The rise of AI tools in recent times, many with available-to-use APIs, has sparked tons of new and fresh SaaS applications. Personally, I think that it’s coming at a perfect time with the shift to more application-based Chrome extensions! While many of the older extensions may be wiped out from this transition, plenty of new ones built around novel SaaS ideas will come to take their place.

Hence, this is an exciting update to hop on and revamp old extensions or build new ones! Personally, I see many possibilities in using APIs that involve AI being used in extensions to enhance a user’s browsing experience. But that’s really just the tip of the iceberg. If you’re looking to really get into things with your own professional extensions or reaching out to companies to build/update extensions for them, I would recommend upgrading your Gmail account for the benefits it gives in collaborating, developing, and publishing extensions to the Chrome Web Store.

However, remember that every developer’s requirements are different, so learn what you need to keep your current extensions afloat, or your new ones going!

How to Transition to Manifest V3 for Chrome Extensions originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.


, , ,

Animated Background Stripes That Transition on Hover

How often to do you reach for the CSS background-size property? If you’re like me — and probably lots of other front-end folks — then it’s usually when you background-size: cover an image to fill the space of an entire element.

Well, I was presented with an interesting challenge that required more advanced background sizing: background stripes that transition on hover. Check this out and hover it with your cursor:

There’s a lot more going on there than the size of the background, but that was the trick I needed to get the stripes to transition. I thought I’d show you how I arrived there, not only because I think it’s a really nice visual effect, but because it required me to get creative with gradients and blend modes that I think you might enjoy.

Let’s start with a very basic setup to keep things simple. I’m talking about a single <div> in the HTML that’s styled as a green square:

div {   width: 500px;   height: 500px;   background: palegreen; }
Perfect square with a pale green background color.

Setting up the background stripes

If your mind went straight to a CSS linear gradient when you saw those stripes, then we’re already on the same page. We can’t exactly do a repeating gradient in this case since we want the stripes to occupy uneven amounts of space and transition them, but we can create five stripes by chaining five backgrounds on top of our existing background color and placing them to the top-right of the container:

div {   width: 500px;   height: 500px;   background:      linear-gradient(black, black) top right,     linear-gradient(black, black) top 100px right,     linear-gradient(black, black) top 200px right,     linear-gradient(black, black) top 300px right,     linear-gradient(black, black) top 400px right,      palegreen; }

I made horizontal stripes, but we could also go vertical with the approach we’re covering here. And we can simplify this quite a bit with custom properties:

div {   --gt: linear-gradient(black, black);   --n: 100px;    width: 500px;   height: 500px;   background:      var(--gt) top right,     var(--gt) top var(--n) right,     var(--gt) top calc(var(--n) * 2) right,     var(--gt) top calc(var(--n) * 3) right,     var(--gt) top calc(var(--n) * 4) right,      palegreen; }

So, the --gt value is the gradient and --n is a constant we’re using to nudge the stripes downward so they are offset vertically. And you may have noticed that I haven’t set a true gradient, but rather solid black stripes in the linear-gradient() function — that’s intentional and we’ll get to why I did that in a bit.

One more thing we ought to do before moving on is prevent our backgrounds from repeating; otherwise, they’ll tile and fill the entire space:

div {   --gt: linear-gradient(black, black);   --n: 100px;    width: 500px;   height: 500px;   background:      var(--gt) top right,     var(--gt) top var(--n) right,     var(--gt) top calc(var(--n) * 2) right,     var(--gt) top calc(var(--n) * 3) right,     var(--gt) top calc(var(--n) * 4) right,      palegreen;   background-repeat: no-repeat; }

We could have set background-repeat in the background shorthand, but I decided to break it out here to keep things easy to read.

Offsetting the stripes

We technically have stripes, but it’s pretty tough to tell because there’s no spacing between them and they cover the entire container. It’s more like we have a solid black square.

This is where we get to use the background-size property. We want to set both the height and the width of the stripes and the property supports a two-value syntax that allows us to do exactly that. And, we can chain those sizes by comma separating them the same way we did on background.

Let’s start simple by setting the widths first. Using the single-value syntax for background-size sets the width and defaults the height to auto. I’m using totally arbitrary values here, so set the values to what works best for your design:

div {   --gt: linear-gradient(black, black);   --n: 100px;    width: 500px;   height: 500px;   background:      var(--gt) top right,     var(--gt) top var(--n) right,     var(--gt) top calc(var(--n) * 2) right,     var(--gt) top calc(var(--n) * 3) right,     var(--gt) top calc(var(--n) * 4) right,      palegreen;   background-repeat: no-repeat;   background-size: 60%, 90%, 70%, 40%, 10%; }

If you’re using the same values that I am, you’ll get this:

Doesn’t exactly look like we set the width for all the stripes, does it? That’s because of the auto height behavior of the single-value syntax. The second stripe is wider than the others below it, and it is covering them. We ought to set the heights so we can see our work. They should all be the same height and we can actually re-use our --n variable, again, to keep things simple:

div {   --gt: linear-gradient(black, black);   --n: 100px;    width: 500px;   height: 500px;   background:      var(--gt) top right,     var(--gt) top var(--n) right,     var(--gt) top calc(var(--n) * 2) right,     var(--gt) top calc(var(--n) * 3) right,     var(--gt) top calc(var(--n) * 4) right,      palegreen;     background-repeat: no-repeat;     background-size: 60% var(--n), 90% var(--n), 70% var(--n), 40% var(--n), 10% var(--n); // HIGHLIGHT 15 }

Ah, much better!

Adding gaps between the stripes

This is a totally optional step if your design doesn’t require gaps between the stripes, but mine did and it’s not overly complicated. We change the height of each stripe’s background-size a smidge, decreasing the value so they fall short of filling the full vertical space.

We can continue to use our --n variable, but subtract a small amount, say 5px, using calc() to get what we want.

background-size: 60% calc(var(--n) - 5px), 90% calc(var(--n) - 5px), 70% calc(var(--n) - 5px), 40% calc(var(--n) - 5px), 10% calc(var(--n) - 5px);

That’s a lot of repetition we can eliminate with another variable:

div {   --h: calc(var(--n) - 5px);   /* etc. */   background-size: 60% var(--h), 90% var(--h), 70% var(--h), 40% var(--h), 10% var(--h); }

Masking and blending

Now let’s swap the palegreen background color we’ve been using for visual purposes up to this point for white.

div {   /* etc. */   background:      var(--gt) top right,     var(--gt) top var(--n) right,     var(--gt) top calc(var(--n) * 2) right,     var(--gt) top calc(var(--n) * 3) right,     var(--gt) top calc(var(--n) * 4) right,      #fff;   /* etc. */ }

A black and white pattern like this is perfect for masking and blending. To do that, we’re first going to wrap our <div> in a new parent container and introduce a second <div> under it:

<section>   <div></div>   <div></div> </section>

We’re going to do a little CSS re-factoring here. Now that we have a new parent container, we can pass the fixed width and height properties we were using on our <div> over there:

section {   width: 500px;   height: 500px; } 

I’m also going to use CSS Grid to position the two <div> elements on top of one another. This is the same trick Temani Afif uses to create his super cool image galleries. The idea is that we place both divs over the full container using the grid-area property and align everything toward the center:

section {   display: grid;   align-items: center;   justify-items: center;   width: 500px;   height: 500px; }   section > div {   width: inherit;   height: inherit;   grid-area: 1 / 1; }

Now, check this out. The reason I used a solid gradient that goes from black to black earlier is to set us up for masking and blending the two <div> layers. This isn’t true masking in the sense that we’re calling the mask property, but the contrast between the layers controls what colors are visible. The area covered by white will remain white, and the area covered by black leaks through. MDN’s documentation on blend modes has a nice explanation of how this works.

To get that working, I’ll apply the real gradient we want to see on the first <div> while applying the style rules from our initial <div> on the new one, using the :nth-child() pseudo-selector:

div:nth-child(1) {    background: linear-gradient(to right, red, orange);  }  div:nth-child(2)  {   --gt: linear-gradient(black, black);   --n: 100px;   --h: calc(var(--n) - 5px);   background:      var(--gt) top right,     var(--gt) top var(--n) right,     var(--gt) top calc(var(--n) * 2) right,     var(--gt) top calc(var(--n) * 3) right,     var(--gt) top calc(var(--n) * 4) right,      white;   background-repeat: no-repeat;   background-size: 60% var(--h), 90% var(--h), 70% var(--h), 40% var(--h), 10% var(--h); }

If we stop here, we actually won’t see any visual difference from what we had before. That’s because we haven’t done the actual blending yet. So, let’s do that now using the screen blend mode:

div:nth-child(2)  {   /* etc. */   mix-blend-mode: screen; }

I used a beige background color in the demo I showed at the beginning of this article. That slightly darker sort of off-white coloring allows a little color to bleed through the rest of the background:

The hover effect

The last piece of this puzzle is the hover effect that widens the stripes to full width. First, let’s write out our selector for it. We want this to happen when the parent container (<section> in our case) is hovered. When it’s hovered, we’ll change the background size of the stripes contained in the second <div>:

/* When <section> is hovered, change the second div's styles */ section:hover > div:nth-child(2){   /* styles go here */ }

We’ll want to change the background-size of the stripes to the full width of the container while maintaining the same height:

section:hover > div:nth-child(2){   background-size: 100% var(--h); }

That “snaps” the background to full-width. If we add a little transition to this, then we see the stripes expand on hover:

section:hover > div:nth-child(2){   background-size: 100% var(--h);   transition: background-size 1s; }

Here’s that final demo once again:

I only added text in there to show what it might look like to use this in a different context. If you do the same, then it’s worth making sure there’s enough contrast between the text color and the colors used in the gradient to comply with WCAG guidelines. And while we’re touching briefly on accessibility, it’s worth considering user preferences for reduced motion when it comes to the hover effect.

That’s a wrap!

Pretty neat, right? I certainly think so. What I like about this, too, is that it’s pretty maintainable and customizable. For example, we can alter the height, colors, and direction of the stripes by changing a few values. You might even variablize a few more things in there — like the colors and widths — to make it even more configurable.

I’m really interested if you would have approached this a different way. If so, please share in the comments! It’d be neat to see how many variations we can collect.

Animated Background Stripes That Transition on Hover originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.


, , , ,

How to Build Your First Custom Svelte Transition

The Svelte transition API provides a first-class way to animate your components when they enter or leave the document, including custom Svelte transitions. By default, the transition directive uses CSS animations, which generally offer better performance and allow the browser’s main thread to remain unblocked. The API is as simple as this: <element transition:transitionFunction />. You can also specify in or out directives which are uni-directional transitions, only running when the element is mounted or unmounted.

An animated example of a custom Svelte transition showing a to do list. An item is typed and animated into the list of items when entered. Clicking a done button animates the item out of view.
Example of a working Svelte transition (jump to demo)

Svelte offers a runtime svelte/transition package that ships with seven prepackaged Svelte transition functions, all of which can be dropped in and tweaked to your heart’s desire. Pairing this with the svelte/easing package, allows for a wide swath of interactions, without writing any of the transition code yourself. Play around with different transitions and easing functions to get a feel for what is possible.

Looking for instructions on how to get started with Svelte? We have a solid overview for you to check out.

The Svelte Custom Transition API

If you need even more control than what the Svelte Transition API offers out of the box, Svelte permits you to specify your own custom transition function, as long as you adhere to a few conventions. From the docs, here’s what the custom transition API looks like:

transition = (node: HTMLElement, params: any) => {   delay?: number,   duration?: number,   easing?: (t: number) => number,   css?: (t: number, u: number) => string,   tick?: (t: number, u: number) => void } 

Let’s break it down. A transition function takes a reference to the DOM node where the transition directive is used and returns an object with some parameters that control the animation and, most importantly, a css or tick function.

The css function’s job is to return a string of CSS that describes the animation, typically including some kind of transform or opacity change. Alternatively, you can opt to return a tick function, which lets you control every aspect of the animation with the power JavaScript, but pays a performance penalty since this type of transition does not use CSS animations.

Both the css and tick functions take two parameters called (t, u) by convention. t is a decimal number that travels from 0.00 to 1.00 while the element is entering the DOM and from 1.00 back to 0.00 when the element is leaving. The u parameter is the inverse of t or 1 - t at any given moment. For example, if you return a string of transform: scale($ {t}), your element would smoothly animate from 0 to 1 on enter, and vice versa on exit.

These concepts may seem a bit abstract, so let’s solidify them by building our own custom Svelte transition!

Building your first custom Svelte transition

First, let’s set up some boilerplate that allows us to toggle an element’s existence in the DOM using a Svelte #if block. Remember, Svelte transitions only run when an element is actually leaving or entering the DOM.

<script>   let showing = true </script>  <label for="showing">   Showing </label> <input id="showing" type="checkbox" bind:checked={showing} />  {#if showing}   <h1>Hello custom transition!</h1> {/if}

You should be able to toggle the checkbox and see our element starkly appear and disappear in place.

Next, let’s set up our custom Svelte transition function and get it wired up to our element.

<script>   let showing = true   // Custom transition function   function whoosh(node) {     console.log(node)   } </script>  <label for="showing">   Showing </label> <input id="showing" type="checkbox" bind:checked={showing} />  {#if showing}   <h1 transition:whoosh>Hello custom transition!</h1> {/if}

Now, if you toggle the checkbox, you will see the <h1> element logged to the console. This proves we have the custom transition connected properly! We won’t actually use the DOM node in our example, but it’s often useful to have access to the element to reference its current styles or dimensions.

For our element to do any animation at all, we need to return an object that contains a css (or tick) function. Let’s have our css function return a single line of CSS that scales our element. We’ll also return a duration property that controls how long the animation takes.

<script>   function swoop() {     return {       duration: 1000,       css: () => `transform: scale(.5)`     }   }   let showing = true </script>  <!-- markup -->

We’ve got something moving! You will notice our element jumps straight to .5 scale when toggling the checkbox. This is something, but it would feel much better if it smoothly transitioned. That’s where the (t, u) parameters come in.

<script>   function swoop() {     return {       duration: 1000,       css: (t) => `transform: scale($ {t})`     }   }   let showing = true </script>  <!-- markup -->

Now we are talking! Remember, t rolls smoothly from 0.00 to 1.00 when an element enters, and vice versa when it leaves. This allows us to achieve the smooth effect we want. In fact, what we just wrote is essentially the built-in scale transition from the svelte/transition package.

Let’s get a little bit fancier. To live up to our custom Svelte transition’s namesake, swoop, let’s add a translateX to our transform, so that our element zooms in and out from the side.

I want to challenge you to attempt the implementation first before we continue. Trust me, it will be fun! Assume that we want to translate to 100% when the element is leaving and back to 0% when it enters.


How did it go? Want to compare answers?

Here’s what I got:

css: (t, u) => `transform: scale($ {t}) translateX($ {u * 100}%);`

It’s okay if you have something different! Let me break down what I did.

The key thing here is the usage of the second parameter in the css function. If we think about our animation while the element is entering the screen, we want to end up at scale(1) translateX(0%), so we can’t use unaltered t for both the scale and the transform. This is the convenience behind the u parameter — it is the inverse of t at any given moment, so we know it will be 0 when t is 1! I then multiplied u by 100 to get the percentage value and tacked on the % sign at the end.

Learning the interplay between t and u is an important piece of the custom transition puzzle in Svelte. These two parameters enable a world of dynamism for your animations; they can be divided, multiplied, twisted, or contorted into whatever needs you have.

Let’s slap my favorite svelte/easing function on our transition and call it a day:

<script>   import { elasticOut } from 'svelte/easing'   function swoop() {     return {       duration: 1000,       easing: elasticOut,       css: (t, u) => `transform: scale($ {t}) translateX($ {u * 100}%)`     }   }   let showing = true </script>  <label for="showing">   Showing </label> <input id="showing" type="checkbox" bind:checked={showing} />  {#if showing}   <h1 transition:swoop>Hello custom transition!</h1> {/if}

Wrapping up

Congratulations! You can now build a custom Svelte transition function. We have only scratched the surface of what is possible but I hope you feel equipped with the tools to explore even further. I would highly recommend reading the docs and going through the official tutorial to gain even more familiarity.

How to Build Your First Custom Svelte Transition originally published on CSS-Tricks. You should get the newsletter and become a supporter.


, , , ,

Nailing That Cool Dissolve Transition

We’re going to create an impressive transition effect between images that’s, dare I say, very simple to implement and apply to any site. We’ll be using the kampos library because it’s very good at doing exactly what we need. We’ll also explore a few possible ways to tweak the result so that you can make it unique for your needs and adjust it to the experience and impression you’re creating.

Take one look at the Awwwards Transitions collection and you’ll get a sense of how popular it is to do immersive effects, like turning one media item into another. Many of those examples use WebGL for the job. Another thing they have in common is the use of texture mapping for either a displacement or dissolve effect (or both).

To make these effects, you need the two media sources you want to transition from and to, plus one more that is the map, or a grid of values for each pixel, that determines when and how much the media flips from one image to the next. That map can be a ready-made image, or a <canvas> that’s drawn upon, say, noise. Using a dissolve transition effect by applying a noise as a map is definitely one of those things that can boost that immersive web experience. That’s what we’re after.

Setting up the scene

Before we can get to the heavy machinery, we need a simple DOM scene. Two images (or videos, if you prefer), and the minimum amount of JavaScript to make sure they’re loaded and ready for manipulation.

<main>   <section>     <figure>       <canvas id="target">         <img id="source-from" src="path/to/first.jpg" alt="My first image" />         <img id="source-to" data-src="path/to/second.jpg" alt="My second image" />       </canvas>     <figure>   </section> </main>

This will give us some minimal DOM to work with and display our scene. The stage is ready; now let’s invite in our main actors, the two images:

// Notify when our images are ready function loadImage (src) {   return new Promise(resolve => {     const img = new Image();     img.onload = function () {       resolve(this);     };     img.src = src;   }); } // Get the image URLs const imageFromSrc = document.querySelector('#source-from').src; const imageToSrc = document.querySelector('#source-to').dataset.src; // Load images  and keep their promises so we know when to start const promisedImages = [   loadImage(imageFromSrc),   loadImage(imageToSrc) ];

Creating the dissolve map

The scene is set, the images are fetched — let’s make some magic! We’ll start by creating the effects we need. First, we create the dissolve map by creating some noise. We’ll use a Classic Perlin noise inside a turbulence effect which kind of stacks noise in different scales, one on top of the other, and renders it onto a <canvas> in grayscale:

const turbulence = kampos.effects.turbulence({ noise: kampos.noise.perlinNoise });

This effect kind of works like the SVG feTurbulence filter effect. There are some good examples of this in “Creating Patterns With SVG Filters” from Bence Szabó.

Second, we set the initial parameters of the turbulence effect. These can be tweaked later for getting the specific desired visuals we might need per case:

// Depending of course on the size of the target canvas const WIDTH = 854; const HEIGHT = 480; const CELL_FACTOR = 2; const AMPLITUDE = CELL_FACTOR / WIDTH;  turbulence.frequency = {x: AMPLITUDE, y: AMPLITUDE}; turbulence.octaves = 1; turbulence.isFractal = true;

This code gives us a nice liquid-like, or blobby, noise texture. The resulting transition looks like the first image is sinking into the second image. The CELL_FACTOR value can be increased to create a more dense texture with smaller blobs, while the octaves=1 is what’s keeping the noise blobby. Notice we also normalize the amplitude to at least the larger side of the media, so that texture is stretched nicely across our image.

Next we render the dissolve map. In order to be able to see what we got, we’ll use the canvas that’s already in the DOM, just for now:

const mapTarget = document.querySelector('#target'); // instead of document.createElement('canvas'); mapTarget.width = WIDTH; mapTarget.height = HEIGHT;  const dissolveMap = new kampos.Kampos({   target: mapTarget,   effects: [turbulence],   noSource: true }); dissolveMap.draw(); 


We are going to pause here and examine how changing the parameters above affects the visual results. Now, let’s tweak some of the noise configurations to get something that’s more smoke-like, rather than liquid-like, say:

const CELL_FACTOR = 4; // instead of 2

And also this:

turbulence.octaves = 8; // instead of 1

Now we have a more a dense pattern with eight levels (instead of one) superimposed, giving much more detail:

Fantastic! Now back to the original values, and onto our main feature…

Creating the transition

It’s time to create the transition effect:

const dissolve = kampos.transitions.dissolve(); dissolve.map = mapTarget; dissolve.high = 0.03; // for liquid-like effect

Notice the above value for high? This is important for getting that liquid-like results. The transition uses a step function to determine whether to show the first or second media. During that step, the transition is done smoothly so that we get soft edges rather than jagged ones. However, we keep the low edge of the step at 0.0 (the default). You can imagine a transition from 0.0 to 0.03 is very abrupt, resulting in a rapid change from one media to the next. Think of it as clipping.

On the other hand, if the range was 0.0 to 0.5, we’d get a wider range of “transparency,” or a mix of the two images — like we would get with partial opacity — and we’ll get a smoke-like or “cloudy” effect. We’ll try that one in just a moment.

Before we continue, we must remember to replace the canvas we got from the document with a new one we create off the DOM, like so:

const mapTarget = document.createElement('canvas');

Plug it in, and… action!

We’re almost there! Let’s create our compositor instance:

const target = document.querySelector(‘#target’); const hippo = new kampos.Kampos({target, effects: [dissolve]});

And finally, get the images and play the transition:

Promise.all(promisedImages).then(([fromImage, toImage]) => {   hippo.setSource({media: fromImage, width, height});   dissolve.to = toImage;   hippo.play(time => {     // a sin() to play in a loop     dissolve.progress = Math.abs(Math.sin(time * 4e-4)); // multiply time by a factor to slow it down a bit   }); });


Special effects

OK, we got that blobby goodness. We can try playing a bit with the parameters to get a whole different result. For example, maybe something more smoke-like:

const CELL_FACTOR = 4; turbulence.octaves = 8;

And for a smoother transition, we’ll raise the high edge of the transition’s step function:

dissolve.high = 0.3;

Now we have this:

Extra special effects

And, for our last plot twist, let’s also animate the noise itself! First, we need to make sure kampos will update the dissolve map texture on every frame, which is something it doesn’t do by default:

dissolve.textures[1].update = true;

Then, on each frame, we want to advance the turbulence time property, and redraw it. We’ll also slow down the transition so we can see the noise changing while the transition takes place:

hippo.play(time => {   turbulence.time = time * 2;   dissolveMap.draw();   // Notice that the time factor is smaller here   dissolve.progress = Math.abs(Math.sin(time * 2e-4)); });

And we get this:

That’s it!

Exit… stage right

This is just one example of what we can do with kampos for media transitions. It’s up to you now to mix the ingredients to get the most mileage out of it. Here are some ideas to get you going:

  • Transition between site/section backgrounds
  • Transition between backgrounds in an image carousel
  • Change background in reaction to either a click or hover
  • Remove a custom poster image from a video when it starts playing

Whatever you do, be sure to give us a shout about it in the comments.

The post Nailing That Cool Dissolve Transition appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , ,