@jonathankablan/noraivoire-fx-scroll 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +64 -0
- package/README.md +15 -2
- package/package.json +30 -4
- package/src/index.js +105 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
NORAIVOIRE — LICENCE PROPRIÉTAIRE
|
|
2
|
+
Copyright (c) 2026 Jonathan KABLAN. Tous droits réservés.
|
|
3
|
+
|
|
4
|
+
Le présent logiciel, incluant sans limitation son code source, ses spécifications
|
|
5
|
+
(fichiers .nora.json), son compilateur, son moteur d'effets, ses adaptateurs, ses
|
|
6
|
+
modèles (templates), sa documentation, ses jeux de tests et ses éléments graphiques
|
|
7
|
+
(ci-après « le Logiciel »), est la propriété exclusive de Jonathan KABLAN
|
|
8
|
+
(ci-après « le Titulaire »).
|
|
9
|
+
|
|
10
|
+
1. ABSENCE DE LICENCE OPEN SOURCE
|
|
11
|
+
Le Logiciel n'est pas distribué sous une licence open source. Aucun droit
|
|
12
|
+
n'est concédé implicitement, par estoppel ou autrement. La simple mise à
|
|
13
|
+
disposition du Logiciel, y compris dans un dépôt privé ou un registre de
|
|
14
|
+
paquets privé, ne vaut pas concession de licence.
|
|
15
|
+
|
|
16
|
+
2. USAGE AUTORISÉ
|
|
17
|
+
Tout usage du Logiciel est subordonné à un contrat de licence écrit et signé
|
|
18
|
+
par le Titulaire (ci-après « Contrat de Licence »). En l'absence d'un tel
|
|
19
|
+
contrat, seul le Titulaire est autorisé à utiliser le Logiciel.
|
|
20
|
+
|
|
21
|
+
3. INTERDICTIONS
|
|
22
|
+
Sauf autorisation écrite et préalable du Titulaire, il est interdit de :
|
|
23
|
+
a) copier, reproduire, modifier, adapter, traduire ou créer des œuvres
|
|
24
|
+
dérivées du Logiciel, en tout ou partie ;
|
|
25
|
+
b) distribuer, publier, sous-licencier, louer, prêter, vendre ou mettre à
|
|
26
|
+
disposition le Logiciel à des tiers, y compris sous forme de service
|
|
27
|
+
(SaaS) ou intégré dans un autre produit distribué ;
|
|
28
|
+
c) décompiler, désassembler ou procéder à de l'ingénierie inverse du
|
|
29
|
+
Logiciel, sauf dans les limites strictement prévues par l'article
|
|
30
|
+
L.122-6-1 du Code de la propriété intellectuelle ;
|
|
31
|
+
d) supprimer ou altérer les mentions de propriété, de copyright ou la
|
|
32
|
+
présente licence ;
|
|
33
|
+
e) utiliser le Logiciel, ses spécifications ou sa documentation pour
|
|
34
|
+
entraîner, affiner ou évaluer un modèle d'intelligence artificielle
|
|
35
|
+
destiné à des tiers, ou pour concevoir un produit concurrent.
|
|
36
|
+
|
|
37
|
+
4. PRODUITS GÉNÉRÉS
|
|
38
|
+
Les fichiers produits par le compilateur NoraIvoire pour le compte d'un
|
|
39
|
+
licencié (CSS, Dart, TypeScript générés) peuvent être intégrés dans les
|
|
40
|
+
applications de ce licencié dans les conditions fixées par son Contrat de
|
|
41
|
+
Licence. Ils ne peuvent pas être redistribués en tant que bibliothèque,
|
|
42
|
+
framework ou kit de design autonome.
|
|
43
|
+
|
|
44
|
+
5. CONFIDENTIALITÉ
|
|
45
|
+
Le Logiciel contient des informations confidentielles et des secrets
|
|
46
|
+
d'affaires du Titulaire. Toute personne y ayant accès s'engage à en
|
|
47
|
+
préserver la confidentialité.
|
|
48
|
+
|
|
49
|
+
6. ABSENCE DE GARANTIE
|
|
50
|
+
Sauf stipulation contraire du Contrat de Licence, le Logiciel est fourni
|
|
51
|
+
« EN L'ÉTAT », sans garantie d'aucune sorte, expresse ou implicite. Dans
|
|
52
|
+
les limites autorisées par la loi, le Titulaire ne saurait être tenu
|
|
53
|
+
responsable d'aucun dommage résultant de l'utilisation du Logiciel.
|
|
54
|
+
|
|
55
|
+
7. COMPOSANTS TIERS
|
|
56
|
+
Le Logiciel peut référencer des composants tiers soumis à leurs propres
|
|
57
|
+
licences, listés dans THIRD_PARTY_NOTICES.md. La présente licence ne
|
|
58
|
+
modifie pas ces licences.
|
|
59
|
+
|
|
60
|
+
8. DROIT APPLICABLE
|
|
61
|
+
La présente licence est régie par le droit français. Tout litige relève de
|
|
62
|
+
la compétence des tribunaux du ressort de la Cour d'appel de Paris.
|
|
63
|
+
|
|
64
|
+
Contact licences : jonathan.kablan@gmail.com
|
package/README.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @jonathankablan/noraivoire-fx-scroll
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Effets de défilement optionnels de NoraIvoire 0.1.1 : `pin`, `scrub`, `parallax`. Chargés à la demande, aucun effet de bord à l'import, ≤ 2 Kio gzip, mouvement réduit respecté.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<link rel="stylesheet" href="node_modules/@jonathankablan/noraivoire-core/dist/fx/fx-scroll.css">
|
|
7
|
+
<script type="module">
|
|
8
|
+
import * as moteur from '@jonathankablan/noraivoire-core/engine';
|
|
9
|
+
import { install } from '@jonathankablan/noraivoire-fx-scroll';
|
|
10
|
+
install(moteur);
|
|
11
|
+
moteur.start();
|
|
12
|
+
</script>
|
|
13
|
+
<img data-fx="parallax" data-fx-parallax="speed:30" src="…" alt="…">
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Logiciel propriétaire © 2026 Jonathan KABLAN, tous droits réservés (voir LICENSE). La mise à disposition sur npm ne vaut pas concession de licence : tout usage est subordonné à un contrat de licence écrit.
|
package/package.json
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jonathankablan/noraivoire-fx-scroll",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"license": "UNLICENSED",
|
|
5
|
+
"description": "Effets de défilement optionnels NoraIvoire (pin, scrub, parallax), chargés à la demande. Propriétaire.",
|
|
6
|
+
"author": "Jonathan KABLAN",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "src/index.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./src/index.js"
|
|
11
|
+
},
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=20"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"noraivoire",
|
|
19
|
+
"scroll",
|
|
20
|
+
"parallax",
|
|
21
|
+
"effects"
|
|
22
|
+
],
|
|
23
|
+
"files": [
|
|
24
|
+
"src",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public",
|
|
30
|
+
"registry": "https://registry.npmjs.org/"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// NoraIvoire — Effets de défilement optionnels : pin, scrub, parallax.
|
|
2
|
+
// © 2026 Jonathan KABLAN — Propriétaire, tous droits réservés.
|
|
3
|
+
//
|
|
4
|
+
// Mêmes règles que le moteur (vérifiées par `nora audit`) : aucune écriture hors
|
|
5
|
+
// des variables --fx-* (ctx.set) et des attributs data-fx-*, aucun effet de bord
|
|
6
|
+
// à l'import. Les effets sont enregistrés par install(moteur), à la demande :
|
|
7
|
+
// const fx = await import('…/fx/fx-scroll.js'); fx.install(moteur);
|
|
8
|
+
|
|
9
|
+
const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
|
|
10
|
+
|
|
11
|
+
/** Progression de l'élément dans la fenêtre : 0 quand il entre par le bas, 1 quand il sort par le haut. */
|
|
12
|
+
export function viewProgress(top, height, viewport) {
|
|
13
|
+
const vh = viewport > 0 ? viewport : 1;
|
|
14
|
+
return clamp((vh - top) / (vh + Math.max(0, height)), 0, 1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Décalage de parallaxe en pixels pour une progression donnée. */
|
|
18
|
+
export function parallaxOffset(progress, speed, reverse = false) {
|
|
19
|
+
return (0.5 - progress) * speed * 4 * (reverse ? -1 : 1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Écoute passive du défilement, regroupée par rAF, suspendue hors de la fenêtre. */
|
|
23
|
+
function onScroll(el, ctx, update) {
|
|
24
|
+
const run = () => ctx.frame(update);
|
|
25
|
+
window.addEventListener('scroll', run, { passive: true, signal: ctx.signal });
|
|
26
|
+
window.addEventListener('resize', run, { passive: true, signal: ctx.signal });
|
|
27
|
+
if (typeof IntersectionObserver !== 'undefined') {
|
|
28
|
+
ctx.onVisible((visible) => { ctx.visible = visible; if (visible) run(); });
|
|
29
|
+
}
|
|
30
|
+
run();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const nativeTimeline = () => typeof CSS !== 'undefined' && CSS.supports?.('animation-timeline: view()') === true;
|
|
34
|
+
|
|
35
|
+
export const effects = Object.freeze({
|
|
36
|
+
pin: {
|
|
37
|
+
options: { top: { type: 'number', default: 0, min: 0, max: 400 } },
|
|
38
|
+
mount(el, opts, ctx) {
|
|
39
|
+
// L'épinglage lui-même est en CSS (position: sticky) : il fonctionne sans JS.
|
|
40
|
+
ctx.set('--fx-pin-top', `${opts.top}px`);
|
|
41
|
+
const track = el.parentElement;
|
|
42
|
+
if (!track) return () => el.style.removeProperty('--fx-pin-top');
|
|
43
|
+
onScroll(el, ctx, () => {
|
|
44
|
+
const t = track.getBoundingClientRect();
|
|
45
|
+
const r = el.getBoundingClientRect();
|
|
46
|
+
const room = t.height - r.height;
|
|
47
|
+
const p = room > 0 ? clamp((opts.top - t.top) / room, 0, 1) : 0;
|
|
48
|
+
ctx.set('--fx-pin-progress', p.toFixed(4));
|
|
49
|
+
el.toggleAttribute('data-fx-pinned', p > 0 && p < 1);
|
|
50
|
+
});
|
|
51
|
+
return () => {
|
|
52
|
+
el.style.removeProperty('--fx-pin-top');
|
|
53
|
+
el.style.removeProperty('--fx-pin-progress');
|
|
54
|
+
el.removeAttribute('data-fx-pinned');
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
scrub: {
|
|
60
|
+
options: {
|
|
61
|
+
start: { type: 'number', default: 0, min: 0, max: 100 },
|
|
62
|
+
end: { type: 'number', default: 100, min: 0, max: 100 },
|
|
63
|
+
},
|
|
64
|
+
mount(el, opts, ctx) {
|
|
65
|
+
const cleanup = () => el.style.removeProperty('--fx-progress');
|
|
66
|
+
// Mouvement réduit : état final immédiat, aucun contenu masqué.
|
|
67
|
+
if (ctx.reducedMotion) { ctx.set('--fx-progress', '1'); return cleanup; }
|
|
68
|
+
const a = Math.min(opts.start, opts.end) / 100;
|
|
69
|
+
const span = Math.max(opts.end / 100 - a, 0.0001);
|
|
70
|
+
onScroll(el, ctx, () => {
|
|
71
|
+
const r = el.getBoundingClientRect();
|
|
72
|
+
const p = clamp((viewProgress(r.top, r.height, window.innerHeight) - a) / span, 0, 1);
|
|
73
|
+
ctx.set('--fx-progress', p.toFixed(4));
|
|
74
|
+
});
|
|
75
|
+
return cleanup;
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
parallax: {
|
|
80
|
+
options: {
|
|
81
|
+
speed: { type: 'number', default: 20, min: 0, max: 100 },
|
|
82
|
+
reverse: { type: 'boolean', default: false },
|
|
83
|
+
},
|
|
84
|
+
mount(el, opts, ctx) {
|
|
85
|
+
if (ctx.reducedMotion) return;
|
|
86
|
+
const cleanup = () => { el.style.removeProperty('--fx-speed'); el.style.removeProperty('--fx-parallax'); };
|
|
87
|
+
// Le CSS natif (animation-timeline: view()) fait le travail : on ne transmet que la vitesse.
|
|
88
|
+
if (nativeTimeline()) { ctx.set('--fx-speed', opts.reverse ? -opts.speed : opts.speed); return cleanup; }
|
|
89
|
+
onScroll(el, ctx, () => {
|
|
90
|
+
const r = el.getBoundingClientRect();
|
|
91
|
+
const off = parallaxOffset(viewProgress(r.top, r.height, window.innerHeight), opts.speed, opts.reverse);
|
|
92
|
+
ctx.set('--fx-parallax', `${off.toFixed(1)}px`);
|
|
93
|
+
});
|
|
94
|
+
return cleanup;
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
/** Enregistre pin, scrub et parallax dans le moteur fourni. Idempotent. */
|
|
100
|
+
export function install(engine) {
|
|
101
|
+
if (!engine || typeof engine.defineEffect !== 'function') throw new TypeError('NoraIvoire : install() attend le module du moteur');
|
|
102
|
+
for (const [name, def] of Object.entries(effects)) {
|
|
103
|
+
if (!engine.hasEffect?.(name)) engine.defineEffect(name, def);
|
|
104
|
+
}
|
|
105
|
+
}
|