Skip to content

Commit

Permalink
add Scroll
Browse files Browse the repository at this point in the history
- refactor property and target value management the prop now calls the target instead of the otherway around
  • Loading branch information
soerenmeier committed Dec 4, 2023
1 parent 74a1cf0 commit 91d693b
Show file tree
Hide file tree
Showing 8 changed files with 419 additions and 79 deletions.
10 changes: 5 additions & 5 deletions src/animation/animation.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,29 +125,29 @@ class PropertyAnimation {

this.from = this.iniFrom;
if (!this.from)
this.from = target.getValue(this.prop);
this.from = this.prop.getValue(target);

this.to = this.iniTo;
if (!this.to)
this.to = target.getValue(this.prop);
this.to = this.prop.getValue(target);

if (this.to.unit !== this.from.unit)
throw new Error(this.from.unit + ' != ' + this.to.unit);
}

restoreBefore(target) {
target.removeValue(this.prop);
this.prop.removeValue(target);
}

render(pos, target) {
if (this.valueFn) {
target.setValue(this.prop, this.prop.parseValue(this.valueFn(pos)));
this.prop.setValue(target, this.prop.parseValue(this.valueFn(pos)));
return;
}

const dif = this.to.num - this.from.num;

target.setValue(this.prop, this.from.cloneAdd(pos * dif));
this.prop.setValue(target, this.from.cloneAdd(pos * dif));
}
}

Expand Down
72 changes: 58 additions & 14 deletions src/animation/property.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@ export default class Property {

// try to access the value from the target
getValue(target) {
return this.defaultValue();
const val = target.getValue(this.name);
if (typeof val === 'undefined' || val === null)
return this.defaultValue();

return val;
}

// if this property is a transform function return its name
transformFunction() {
return null;
setValue(target, val) {
return target.setValue(this.name, val);
}

styleName() {
return null;
removeValue(target) {
// todo remove
return target.removeValue(this.name);
}
}

Expand Down Expand Up @@ -63,8 +67,21 @@ export class Transform extends Property {
return new Value(this._defVal, this._defUnit);
}

transformFunction() {
return this._transformFunction;
getValue(target) {
const val = target.getTransformValue(this._transformFunction);
if (typeof val === 'undefined' || val === null)
return this.defaultValue();

return val;
}

setValue(target, val) {
return target.setTransformValue(this._transformFunction, val);
}

removeValue(target) {
// todo remove
return target.removeTransformValue(this._transformFunction);
}
}

Expand Down Expand Up @@ -92,12 +109,27 @@ export class TransformXY extends Property {
};
}

transformFunction() {
return (transforms, value) => {
transforms['translateX'] = value.x.toString();
transforms['translateY'] = value.y.toString();
getValue(target) {
const x = target.getTransformValue('translateX');
const y = target.getTransformValue('translateY');

const def = this.defaultValue();

return {
x: x ? x : def.x,
y: y ? y : def.y
};
}

setValue(target, val) {
target.setTransformValue('translateX', val.x);
target.setTransformValue('translateY', val.y);
}

removeValue(target) {
target.removeTransformValue('translateX');
target.removeTransformValue('translateY');
}
}

const STYLE_PROPS = {
Expand Down Expand Up @@ -136,8 +168,20 @@ export class StyleProp extends Property {
return Value.parse(style[this.name]);
}

styleName() {
return this.name;
getValue(target) {
const val = target.getStyleValue(this.name);
if (!val)
return this.defaultValue();

return val;
}

setValue(target, val) {
return target.setStyleValue(this.name, val);
}

removeValue(target) {
return target.removeStyleValue(this.name);
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/chnobli.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// import { takeProp } from './utils/internal.js';
import Animation from './animation/public.js';
import Timeline from './timeline/public.js';
import Scroll from './scroll/public.js';
import { stagger as _stagger } from './stagger/stagger.js';


Expand All @@ -19,6 +19,10 @@ export function stagger(value) {
return _stagger(value);
}

export function scroll(props = {}) {
return new Scroll(props);
}

/*
animate(target, {
Expand Down
41 changes: 41 additions & 0 deletions src/scroll/event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export default class ScrollEvent {
constructor() {
this._listeners = new Set;

window.addEventListener('scroll', e => this._onScroll(e));
}

static global() {
if (typeof window === 'undefined')
return null;

if (typeof window.chnobliScroll === 'undefined')
window.chnobliScroll = new ScrollEvent;
return window.chnobliScroll;
}

add(fn) {
this._listeners.add(fn);

return {
remove: () => {
this.remove(fn);
}
};
}

remove(fn) {
this._listeners.delete(fn);
}

_onScroll(e) {
for (const fn of this._listeners) {
fn({
y: window.scrollY,
height: window.innerHeight
}, {
remove: () => this.remove(fn)
});
}
}
}
17 changes: 17 additions & 0 deletions src/scroll/public.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Scroll from './scroll.js';

export default class PublicScroll {
/*
{
start: el | px
startView: 'top'
}
*/
constructor(props = {}) {
this._inner = new Scroll(props);
}

add(timeline) {
this._inner.addTimeline(timeline);
}
}
151 changes: 150 additions & 1 deletion src/scroll/scroll.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { takeProp } from '../utils/internal.js';
import { pageOffset } from '../utils/utils.js';
import ScrollEvent from './event.js';

/*
run animation when in view (start stop resume when different events happen)
Expand All @@ -17,4 +21,149 @@ skew on scroll
scroll pin (once in trigger zone, elements keeps it position relative to start of trigger)
parallax
*/
*/


export default class Scroll {
constructor(props = {}) {
// { target, offset }
this._start = parseStart(takeProp(props, 'start', null));
// { target, offset }
this._end = parseEnd(takeProp(props, 'end', null));
this._globalEvent = ScrollEvent.global();

this.start = {
// px
y: 0,
// percentage
view: parseOffset(takeProp(props, 'startView', 'top'))
};
this.end = {
// px
y: 0,
// percentage
view: parseOffset(takeProp(props, 'endView', 'bottom'))
}

this.timelines = [];

this._rmEvent = this._globalEvent.add(y => this._onScroll(y)).remove;
this._initialized = false;
}

addTimeline(timeline) {
this.timelines.push(timeline);
}

init() {
const startOffset = pageOffset(this._start.target);
this.start.y = startOffset.top +
startOffset.height * this._start.offset;

if (this._end) {
const offset = pageOffset(this._end.target);
this.end.y = offset.top + offset.height * this._end.offset;
} else {
this.end.y = this.start.y + pageOffset(this._start.target).height;
}
}

_onScroll({ y, height }) {
if (!this._initialized) {
this.init();
this._initialized = true;
}

const start = this.start.y + height * this.start.view;
const end = this.end.y + height * this.end.view;

const dif = end - start;
const x = 1 - (end - y) / dif;

for (const timeline of this.timelines) {
timeline.seek(x);
}
}
}

function parseStart(val) {
// might be an object { target, offset }
if (!val) {
throw new Error(
'unknown start property use an html element or an object'
);
}

let element = null;
let offset = 'top';

if (typeof val === 'object') {
if (val instanceof HTMLElement)
element = val;
else if ('target' in val) {
element = val.target;
if (typeof val.offset !== 'undefined')
offset = val.offset;
} else {
throw new Error('start unknown ' + val);
}
} else {
throw new Error('unknown start val');
}

return {
target: element,
offset: parseOffset(offset)
};
}

function parseEnd(val) {
// might be an object { target, offset }
if (!val)
return null;

let element = null;
let offset = 'top';

if (typeof val === 'object') {
if (val instanceof HTMLElement)
element = val;
else if ('target' in val) {
element = val.target;
if (typeof val.offset !== 'undefined')
offset = val.offset;
} else {
throw new Error('start unknown ' + val);
}
} else {
throw new Error('unknown start val');
}

return {
target: element,
offset: parseOffset(offset)
};
}

function parseOffset(val) {
if (typeof val === 'string') {
if (val === 'top')
return 0;

if (val === 'center')
return .5;

if (val === 'bottom')
return 1;

throw new Error('unknown view value ' + val);
}

if (!(typeof val === 'number')) {
throw new Error(
'view expecting to be a top/center/bottom or a percent value'
);
}

return val;
}
Loading

0 comments on commit 91d693b

Please sign in to comment.