Active-CSS / active-css

The epic event-driven browser language for UI with functionality in one-liner CSS. Over 100 incredible CSS commands for DOM manipulation, ajax, reactive variables, single-page application routing, and lots more. Could CSS be the JavaScript framework of the future?
https://activecss.org
Other
42 stars 7 forks source link

New intersect event #322

Closed bob2517-whiteline closed 1 year ago

bob2517-whiteline commented 1 year ago

This would use intersection observer behind the scenes. Currently I have to manually create the intersection observer event and set up a mixture of attributes and classes to get things happening as they appear on the screen. I use it a lot to defer the loading of content until actually needed on the page, and it makes an amazing difference in terms of rendering speed, but currently it's too much of an involved process to document, which is why I haven't shown how to do it. But after 6 months of working with it I think I've worked out the best solution now to make it simpler to implement into ACSS.

Example:

.doorImage:visible {
    add-class: .animationEffect;
}

This syntax gives adequate flexibility to do anything you want when something becomes visible, whether it be a simple fade triggered through adding a class which changes opacity, or some sort of CSS transform, or to trigger a sequence of action commands.

Behind the scenes it would create an invisible draw event which creates the necessary intersection observer and trigger to call the visible event when it kicks in. Should be quite quick to implement, so will attempt it at the weekend and see what happens. It shouldn't have any impact on general performance and will be backward compatible.

dragontheory commented 1 year ago

This is awesome!

bob2517-whiteline commented 1 year ago

Yeah, should be a good one. It took a while to get the syntax right :)

bob2517 commented 1 year ago

The code to load an img only when it is visible, which I use mostly everywhere on new sites, would be written something like this:

img[data-lazy-load]:visible {
    set-attribute: src "{@data-lazy-load}";
}

One thing this syntax doesn't yet handle is the triggering of an event when the image is actually loaded, which I've done in other ways manually. A "load" event would be the way to go for that, but I'm not sure this works yet for images. Will look at that at the weekend too.

The syntax for doing something like that could then become like this (not nailed this down yet):

img[data-lazy-load]:visible {
    set-attribute: src "{@data-lazy-load}";
}
img[data-lazy-load][data-slide-in-from-right]:load {
    add-class: .slideInFromRight;
}

It's a tricky one, as there are two events fired. But I think that should cover the scenarios.

The actual ACSS upgrade would be the support of the visible and the load events. The techniques used with those events would depend on developer preference. But the ability to offer the events without having to tackle the JS intersection observer code manually would be the thing of benefit, as it isn't the easiest thing to get your head around.

As a side note, it isn't only images that could have things happen to them when they become visible. I've also used the manual method to bounce text onto the screen when it gets into the page visibly, ie. add a class to the text container when it is visible. There's no point in the text bouncing if no one can see it, with it being further down the page... So the visible event would handle things like that too.

(Note to self: look at the opposite event too, such as unvisible, for when it goes out of visible range. That would, by default, reset the visible event to trigger again when next visible. The default for the visible event should be to only run once. The developer should be responsible for putting things back to how they want them to be before the visible event runs again, and that would be done in the unvisible event, or whatever the unvisible event ends up being named as.)

bob2517 commented 1 year ago

Thought about it a bit more, and to avoid the confusion of using a general term such as "visible", and for consistency with JavaScript, gonna change the event name to "intersect".

Ie. it will be like an onintersect JavaScript event.

Still working out the finer details, but it should integrate all the features of the intersection observer API, defaulting to the basic functionality of doing something when visible. But I want to have the other options in there that intersection observer offers, by doing something like opening up the other intersection properties in the event itself.

dragontheory commented 1 year ago

Very nice. Great idea.

bob2517 commented 1 year ago

If intersection observer is not supported in the browser any intersect events will get ignored, following CSS support rules of not doing anything with commands that aren't recognised.

bob2517 commented 1 year ago

Basic intersect event now working on the branch.

Example of usage, which slides some text in from the left when it becomes visible by scrolling, by adding a class to it (.animated - could be anything though) when it becomes visible in the viewport:

ACSS

*[data-fade-text-in-from-left]:intersect {
    add-class: .animated;
}

CSS

p {
    margin-top: 1000px;
}

*[data-fade-text-in-from-left] {
    opacity: 0;
    transform: translateX(-20px);
    transition: opacity 1s ease-out, transform 1s ease-out;
}

*[data-fade-text-in-from-left].animated {
    opacity: 1;
    transform: translateX(0);
}

HTML

<p data-fade-text-in-from-left>Slide me in from the left.</p>

That is just the basic default with no additional options. I'm going to get the load event properly working on images next, so that when a src attribute becomes populated by the intersect event setting the attribute, the image can be set to fade in only after it has actually loaded. That should cover the most used basic scenarios on the interwebs, but there will be more to do on this after that.

The reason why I say I covers most scenarios, is that you have the freedom to do whatever you want with the event. Ie. you could have an infinite scroll of "cards" that get populated by an ajax call and then draw another empty card after it with instructions (in attributes or classes containing URLs or references) which runs it again when scrolled to. It can trigger animations at certain points in the page. It's really flexible, and the intersect event only runs once per element.

But there is no unIntersect event, which may be required to reverse the original scenario when scrolled away from, and there is no "ratio" option yet per the JS version, and there is no threshold offset option, all of which will be added soon. Just need to work out the syntaxes.

bob2517 commented 1 year ago

Note, the existing core can already do this, and I've used this before on production code:

.myDiv:if-completely-visible():observe {
    add-class: .noOpacity;
    remove-class: .partialOpacity;
}
.myDiv:not-if-completely-visible():observe {
    add-class: .partialOpacity;
    remove-class: .noOpacity;
}

It doesn't use intersect observer, so the performance won't be as good as this new event, but it's a perfectly adequate solution when there is not a lot else going on on the page.

bob2517 commented 1 year ago

Releasing with basic functionality for now. Closing pending release.