Skip to content
72 changes: 72 additions & 0 deletions demo/map.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Map</title>

<script src="../dist/bundle.js" defer></script>

<style>
:root {
--opacity: 0.3;
font-size: 20px;
font-family: "Helvetica Neue", Helvetica;
}

body {
background-image: conic-gradient(
#0f2257,
#1d41a5,
#1d3782,
#90f1fe,
#1145c1,
#06153e,
#06153e
);
background-size: cover;
background-attachment: fixed;
min-height: 100vh;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
}

div {
margin: 100vh 0;
font-size: 3.4rem;
font-weight: 600;
letter-spacing: -1.2px;
color: #fff;
line-height: 1.22em;
width: 800px;
}

span {
--opacity: 0.3;
opacity: var(--opacity);
}

.pro {
display: block;
margin-top: 2.2rem;
}
</style>
</head>
<body>
<!-- This is an example of using Trigger.js, checkout at https://github.com/triggerjs/trigger -->
<div tg-name="a" tg-ref="container" tg-from="1" tg-to="10" tg-step="1">
<section tg-name="a" tg-map="1,2,3,4,5,6,7,8,9,10: 1">
<span tg-name="opacity" tg-filter="2!" tg-follow="container">A dramatically more powerful camera system.</span>
<span tg-name="opacity" tg-filter="3!" tg-follow="container">A display so responsive, every interaction feels new again.</span>
<span tg-name="opacity" tg-filter="4!" tg-follow="container">The world’s fastest smartphone chip.</span>
<span tg-name="opacity" tg-filter="5!" tg-follow="container">Exceptional durability.</span>
<span tg-name="opacity" tg-filter="6!" tg-follow="container">And a huge leap in battery life.</span>
<span tg-name="opacity" tg-filter="6,7" tg-map="6: 0.3; 7: 1" tg-follow="container" class="pro">Let’s Pro.</span>
</section>
</div>
</body>
</html>
330 changes: 329 additions & 1 deletion dist/bundle.js

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
font-size: 20px;
--blur: 100;
--opacity: 0;
--color: 'black';
}

body {
Expand All @@ -22,7 +23,7 @@
}

.container {
height: 300vh;
width: 300vh;
}

.sticky {
Expand All @@ -32,10 +33,11 @@
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
min-width: 100vh;
position: sticky;
top: 0;
font-size: calc(var(--size) * 1em);
color: var(--color);
}

span {
Expand All @@ -48,13 +50,14 @@
class="container"
tg-name="size"
tg-edge="inset"
tg-direction="horizon"
tg-from="14"
tg-to="20"
tg-bezier="0.23, 1.5, 0.32, 2"
>
<div class="container" tg-name="opacity" tg-from="0" tg-to="1">
<div class="sticky">
<span>Hello.</span>
<span tg-name="color" tg-from="0" tg-to="1" tg-map="1...2: black; 2: red; 3: orange; 4: yellow; 5: green; 6: cyan; 7: blue">Hello.</span>
</div>
</div>
</div>
Expand Down
4 changes: 3 additions & 1 deletion src/bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ export default function (
document
.querySelectorAll<HTMLElement>(`[${prefix}name]`)
.forEach((element) => {
const { top, height } = element.getBoundingClientRect();
const { top, height, left, width } = element.getBoundingClientRect();

element.style.setProperty(`--${prefix}top`, `${top + window.scrollY}`);
element.style.setProperty(`--${prefix}left`, `${left + window.scrollX}`);
element.style.setProperty(`--${prefix}height`, `${height}`);
element.style.setProperty(`--${prefix}width`, `${width}`);

observer && observer.observe(element);
});
Expand Down
8 changes: 8 additions & 0 deletions src/directives/tg-direction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { direction } from '../type';

// Default value
const DEFAULT: direction = 'vertical';

export function get(value?: direction) {
return value ? value : DEFAULT;
}
19 changes: 16 additions & 3 deletions src/directives/tg-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,24 @@ export function get(value?: string, data?: TgElementExtraData) {
if (typeof value === 'string' && value.trim() !== '') {
value.split(';').forEach((pair) => {
let arr = pair.split(':');

if (arr.length === 2) {
if (arr[0].indexOf(',') > -1) {
// check whether they are number or not
const nums = arr[0].split(',')
if (!nums.every(item => /\d+/.test(item.trim()))) {
throw new Error('Multiple keys of map is wrong. Please check whether they are number or not.')
}
// Multiple Values
arr[0].split(',').forEach((key) => {
nums.forEach((key) => {
items[key.trim()] = arr[1].trim();
});
} else if (arr[0].indexOf('...') > -1) {
// Use `...` here to define a range, inspired by Swift range operator.
// Instead of using `-`, we can handle negative numbers easily.
if (!(/(\d+)\.\.\.(\d+)/.test(arr[0].trim()))) {
throw new Error('The format of key range is wrong.')
}

let [from, to] = arr[0]
.split('...')
.map((val) => {
Expand All @@ -30,9 +38,14 @@ export function get(value?: string, data?: TgElementExtraData) {

i += data?.increment || 0.01;
}
} else {
// the number normal case
} else if (/\d+/.test(arr[0].trim())) {
items[arr[0].trim()] = arr[1].trim();
} else {
throw new Error('The map key is wrong.')
}
} else {
throw new Error('Map format is wrong.')
}
});
}
Expand Down
31 changes: 31 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,34 @@ export function decimalsLength(number: number): number {
if (Math.floor(number) === number) return 0;
return number.toString().split('.')[1].length || 0;
}

/**
* Get the value. If it is greater than max, get max, otherwise get min.
* @param {number} value
* @param {number} min min value in the range
* @param {number} max max value in the range
* @returns number
*/
export function getValueInRange(value: number, min: number, max: number) {
return Math.min(
Math.max(value, 0),
1
);
}

/**
* compose functions into one function from the left to the right
* @param {Array<Function>} func
* @returns {Function}
*/
export function compose(...func: Array<Function>): Function {
if (func.length === 0) {
return (args: any) => args;
}

if (func.length === 1) {
return func[0];
}

return func.reduce((a, b) => (...args: Array<any>) => b(a(...args)));
}
67 changes: 23 additions & 44 deletions src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { extractValues } from './directives';
import { getPrefix } from './prefix';
import { FilterValue } from './directives/tg-filter';
import { EdgeOptions } from './directives/tg-edge';
import { TgElement } from './type';
import { easePercentage as ease } from './ease';
import { direction, TgElement } from './type';
import { getPercentage, getMappingValue } from './value';

/**
* This function will be called in observe stage,
Expand All @@ -19,12 +19,15 @@ export function parseAttributes(element: HTMLElement): TgElement {
const style = getComputedStyle(actualElement);
const top = +style.getPropertyValue(`--${prefix}top`);
const height = +style.getPropertyValue(`--${prefix}height`);
const left = +style.getPropertyValue(`--${prefix}left`);
const width = +style.getPropertyValue(`--${prefix}width`);

const name: string = extractValues(element, `${prefix}name`);
const from: number = extractValues(actualElement, `${prefix}from`);
const to: number = extractValues(actualElement, `${prefix}to`);
const steps: number = extractValues(actualElement, `${prefix}steps`);
const step: number = extractValues(actualElement, `${prefix}step`);
const direction: direction = extractValues(actualElement, `${prefix}direction`);
const bezier: string | Array<number> = extractValues(
actualElement,
`${prefix}bezier`
Expand All @@ -35,9 +38,12 @@ export function parseAttributes(element: HTMLElement): TgElement {

const range = Math.abs(to - from);
const increment = step === 0 ? range / steps : step;
const segments = range / increment;
const segments = steps ? steps : range / increment;
const decimals = decimalsLength(increment);
const multiplier = from > to ? -1 : 1;
// get the size and the position changed according to tg-direction
const size = direction === 'vertical' ? height : width;
const position = direction === 'vertical' ? top : left;

const mapping: Record<string, string> = extractValues(
element,
Expand All @@ -51,7 +57,12 @@ export function parseAttributes(element: HTMLElement): TgElement {
return {
el: element,
top,
left,
height,
width,
direction,
size,
position,
name,
from,
to,
Expand All @@ -76,59 +87,26 @@ export function parseAttributes(element: HTMLElement): TgElement {
* So keep this as light as possible.
*/
export function parseValues(elements: TgElement[]) {
const { scrollTop: scrolled, clientHeight } = document.documentElement;

elements.forEach((element) => {
const {
el,
top,
height,
increment,
segments,
decimals,
multiplier,
name,
from,
// currently unused
// to,
mapping,
filter,
edge,
lastValue,
bezier,
} = element;

// If the name is equal to '_' (--_), skip
if (name === '--_') {
return;
}

// edge is 'cover' by default
let percentage =
edge === 'cover'
? Math.min(
Math.max(
(scrolled + clientHeight - top) / (clientHeight + height),
0
),
1
)
: Math.min(Math.max((scrolled - top) / (height - clientHeight), 0), 1);



// Calculation result value of bezier
percentage = bezier ? ease(bezier, percentage) : percentage;

let value: string | number;
const percentage = getPercentage(element);
const mappingValue = getMappingValue(element, percentage);

const mappingValue = (
from +
Math.floor((segments + 1) * percentage) * increment * multiplier
).toFixed(decimals);
value = +mappingValue;

if (filter.values.length > 0 && !filter.values.includes(value)) {
if (filter.values.length > 0 && !filter.values.includes(mappingValue)) {
// If the mode is 'exact', remove the CSS property
// Setting the lastValue to null to ensure correct comparison below
if (filter.mode === 'exact') {
Expand All @@ -138,12 +116,10 @@ export function parseValues(elements: TgElement[]) {
return;
}

if (typeof mapping[value] !== 'undefined') {
value = mapping[value];
}
const value: string = mapping[mappingValue] ? mapping[mappingValue] : `${mappingValue}`;

if (lastValue != value) {
el.style.setProperty(name, `${value}`);
el.style.setProperty(name, value);
el.dispatchEvent(
new CustomEvent('tg', {
// @ts-ignore
Expand All @@ -158,4 +134,7 @@ export function parseValues(elements: TgElement[]) {
console.log('value', element, value);
}
});
}
}



1 change: 0 additions & 1 deletion src/trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ const Trigger: TriggerType = {
console.warn(`Unable to initialise, document.body does not exist.`);
return;
}

observeElements();
eventListeners();
},
Expand Down
6 changes: 6 additions & 0 deletions src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ export type BezierOption = string | Array<number>
export type CssVariable = `--${string}`;
export type AttributeNumber = `${number}` | number;
export type Prefix = `${string}-`;
export type direction = 'vertical' | 'horizon';
export type CustomDirective<T extends string = string> = `${T}-${string}`;
export type TgDirective = CustomDirective<'tg'>;

export interface TgElement {
el: HTMLElement;
top: number;
left: number;
width: number;
height: number;
direction: direction;
size: number;
position: number;
name: string;
from: number;
to: number;
Expand Down
Loading