From bbf76dd046dc40f8ab46ac0be3dc979d3022d5fe Mon Sep 17 00:00:00 2001 From: Lenin Compres Date: Thu, 24 Oct 2024 13:06:13 -0400 Subject: [PATCH] cardScroll scroll --- DOM.js | 23 +++++++++ src/CardScroll.js | 119 +++++++++++++++++++++++-------------------- src/pages.js | 125 ++++++++++++++++++++++++---------------------- src/style.js | 1 - 4 files changed, 152 insertions(+), 116 deletions(-) diff --git a/DOM.js b/DOM.js index 60cb23b..8acca10 100644 --- a/DOM.js +++ b/DOM.js @@ -83,6 +83,10 @@ Element.prototype.set = function (model, ...args) { else this[STATION] = e => model(e, this); return this; } + if (station === "binder") { + this.binderSet(model); + return this; + } if (model._bonds) model = model.bind(); if (model.binders) { if (DOM.tags.includes(STATION) && !DOM.attributes.includes(STATION)) return this.set({ @@ -418,6 +422,25 @@ function bind(...args) { return DOM.bind(...args); } +Element.prototype.binderSet = function (name, value) { + if (typeof name == 'string') { + let _name = '_' + name; + this[_name] = new Binder(value); + Object.defineProperty(this, name, { + get() { + return this[_name].value; + }, + set(val) { + this[_name].value = val; + }, + }); + return; + } + for (const [key, value] of Object.entries(name)) { + this.binderSet(key, value); + } +} + // global static methods to handle the DOM class DOM { // returns value based on diff --git a/src/CardScroll.js b/src/CardScroll.js index 6adf44a..d6bdbd5 100644 --- a/src/CardScroll.js +++ b/src/CardScroll.js @@ -4,54 +4,58 @@ import { class CardScroll extends HTMLElement { constructor(items = [], SPEED = 500) { - if(!Array.isArray(items)) items = [items]; super(); - this._ITEMS = new Binder([]); - this._SELECTED = new Binder(-1); + // creates binders in the element and setters/getters for their values + this.SPEED = SPEED; + this.scrolling = 0; + this.binderSet({ + items: [], // key as the prop to set/get, and the initial value + selected: -1, // the actual binder will be _selected + }); + const diff = (val, i) => i - val; + const dist = (val, i) => Math.abs(diff(val, i)); + const pct = (val, i) => dist(val, i) / items.length; + const approx = (val, i, goal) => goal * (1 - pct(val, i) / 2) * diff(val, i) / 2; + const approch = (val, i, goal) => Math.abs(approx(val, i, goal)); this.set({ - content: this._ITEMS.as(items => ({ + content: this._items.as(items => ({ margin: "6em auto", position: "relative", width: "20em", minHeight: "30em", - section: items.map((item, i) => { - const diff = val => i - val; - const dist = val => items.length - Math.abs(diff(val)); - const shade = val => Math.abs(diff(val)) / (items.length + 2); - return { + section: items.map((item, i) => ({ + position: 'absolute', + width: "20em", + backgroundColor: "white", + borderRadius: "2.5em", + boxShadow: "1px 1px 3px black", + overflow: "hidden", + border: "solid 1em white", + transition: `ease-out ${SPEED}ms`, + borderColor: this._selected.as(val => val === i ? 'white' : `rgba(34,64,64,${approch(val, i, 1)})`), + cursor: this._selected.as(val => val === i ? 'auto' : 'pointer'), + zIndex: this._selected.as(val => items.length - dist(val, i)), + transform: this._selected.as(val => `rotate(${approx(val, i, 30)}deg)`), + left: this._selected.as(val => val === i ? 0 : val > i ? '-20em' : '33.3em'), + top: this._selected.as(val => val === i ? 0 : '25%'), + fontSize: this._selected.as(val => val === i ? '1em' : `0.6em`), + main: { + pointerEvents: this._selected.as(val => val === i ? 'auto' : 'none'), + content: item, + }, + div: { + top: 0, + left: 0, position: 'absolute', - width: "20em", - backgroundColor: "white", - borderRadius: "2em", - boxShadow: "1px 1px 3px black", - overflow: "hidden", - border: "solid 1em white", + opacity: this._selected.as(val => val === i ? 0 : approch(val, i, 1)), + backgroundColor: COLOR.LINK_DARK, transition: SPEED + "ms", - borderColor: this._SELECTED.as(val => val === i ? 'white' : `rgba(34,64,64,${shade(val)})`), - cursor: this._SELECTED.as(val => val === i ? 'auto' : 'pointer'), - zIndex: this._SELECTED.as(dist), - transform: this._SELECTED.as(val => `rotate(${ 30 * items.length * (1-shade(val)) * diff(val)/items.length}deg)`), - left: this._SELECTED.as(val => val === i ? 0 : val > i ? '-20em' : '33.3em'), - top: this._SELECTED.as(val => val === i ? 0 : '25%'), - fontSize: this._SELECTED.as(val => val === i ? '1em' : `0.6em`), - main: { - pointerEvents: this._SELECTED.as(val => val === i ? 'auto' : 'none'), - content: item, - }, - div: { - top: 0, - left: 0, - position: 'absolute', - opacity: this._SELECTED.as(val => val === i ? 0 : shade(val)), - backgroundColor: COLOR.LINK_DARK, - transition: SPEED + "ms", - width: "100%", - height: "100%", - pointerEvents: "none", - }, - onclick: e => this._SELECTED.value != i ? this.selected += i > this.selected ? 1 : -1 : null, - } - }), + width: "100%", + height: "100%", + pointerEvents: "none", + }, + onclick: e => this.selected < i ? this.next() : this.prev(), + })), b: { color: "black", backgroundColor: "white", @@ -69,38 +73,43 @@ class CardScroll extends HTMLElement { content: [{ left: "-20%", text: "◀", - opacity: this._SELECTED.as(val => val > 0 ? 1 : 0), + opacity: this._selected.as(val => val > 0 ? 1 : 0), }, { text: "▶", right: "-20%", - opacity: this._SELECTED.as(val => val > -1 && items.length > 1 && val < items.length - 1 ? 1 : 0), + opacity: this._selected.as(val => val > -1 && items.length > 1 && val < items.length - 1 ? 1 : 0), }], - onready: me => setTimeout(() => this.selected = 0, 2 * SPEED), }, })) }); - this.items = items; + this.items = Array.isArray(items) ? items : [items]; } - get selected() { - return this._SELECTED.value; + clear() { + this.selected = -1; } - set selected(n) { - this._SELECTED.value = n; + start(delay = 4) { + clearInterval(this.interval); + this.scrolling = 1; + this.interval = setInterval(() => this.scroll(), delay * this.SPEED); } - get items() { - return this._ITEMS.value; + scroll() { + if (!this.scrolling) return clearInterval(this.interval); + this.selected += this.scrolling; + if (this.items.length <= 1) return clearInterval(this.interval); + if (this.selected < 0 || this.selected >= this.items.length - 1) this.scrolling *= -1; } - set items(items) { - if(!Array.isArray(items)) items = [items]; - this._ITEMS.value = items; + next() { + clearInterval(this.interval); + if (this.selected <= this.items.length) this.selected += 1; } - clear() { - this.selected = -1; + prev() { + clearInterval(this.interval); + if (this.selected > 0) this.selected -= 1; } } diff --git a/src/pages.js b/src/pages.js index 7b35cf0..cea8e22 100755 --- a/src/pages.js +++ b/src/pages.js @@ -33,9 +33,16 @@ Copy.add({ } }); +let newsScroll = new CardScroll(news); +newsScroll.start(); +window.addEventListener('hashchange', () => { + newsScroll.clear(); + newsScroll.start() +}) + export const PAGES = { [Copy.KEY.home]: { - section: new CardScroll(news), + section: newsScroll, }, [Copy.KEY.bio]: { section: { @@ -63,7 +70,7 @@ export const PAGES = { a: [NONE, ...val].map(tag => ({ backgroundColor: activeTag.bind({ [tag]: STYLE.COLOR.LINK_DARK, - default: STYLE.COLOR.FADED + default: STYLE.COLOR.PALE }), color: activeTag.bind(val => val === tag ? STYLE.COLOR.PAGE : STYLE.COLOR.LINK), boxShadow: STYLE.SHADOW.NORMAL, @@ -77,71 +84,69 @@ export const PAGES = { } }), span: { - color: STYLE.COLOR.FADED, + color: STYLE.COLOR.PALE, textShadow: STYLE.SHADOW.TEXT, text: activeTag.bind(val => val !== NONE ? projects.filter(p => p.tags.includes(val)).length : projects.length), }, onready: slideDown, }, - section: projects.map((project, i) => { - return { - model: STYLE.PAGE, - fontSize: "1em", - width: "23em", - cursor: "pointer", - boxShadow: DOM.bind(activeProject, val => val === i ? STYLE.SHADOW.HIGHLIGHT : STYLE.SHADOW.NORMAL), - display: activeTag.bind(val => val === NONE || project.tags.includes(val) ? "block" : "none"), - main: { - minHeight: "6.5em", - div: { - float: "left", - height: "6em", - width: "6em", - marginRight: "0.6em", - backgroundImage: `url(${project.img ? project.img : project.folder + "/thumbnail.jpg"})`, - backgroundSize: "cover", - backgroundPosition: "center" - }, - h6: { - marginBottom: "0.25em", - fontWeight: "bold", - text: project.title, - }, - p: project.desc, - }, - ul: { - marginTop: "0.2em", - li: project.tags.sort(sortWords).map(tag => { - if (!allTags.value.includes(tag)) allTags.value = [...allTags.value, tag].sort(sortWords); - return { - borderRadius: "0.25em", - padding: "0.2em 0.4em", - marginRight: "0.2em", - backgroundColor: STYLE.COLOR.PALE, - color: STYLE.COLOR.LINK, - border: "solid 1px", - borderColor: activeTag.bind({ - [tag]: STYLE.COLOR.LINK, - default: STYLE.COLOR.PALE, - }), - display: "inline-block", - text: tag, - click: e => { - activeTag.value = activeTag.value === tag ? NONE : tag; - e.stopPropagation(); - } - } - }) + section: projects.map((project, i) => ({ + model: STYLE.PAGE, + fontSize: "1em", + width: "23em", + cursor: "pointer", + boxShadow: DOM.bind(activeProject, val => val === i ? STYLE.SHADOW.HIGHLIGHT : STYLE.SHADOW.NORMAL), + display: activeTag.bind(val => val === NONE || project.tags.includes(val) ? "block" : "none"), + main: { + minHeight: "6.5em", + div: { + float: "left", + height: "6em", + width: "6em", + marginRight: "0.6em", + backgroundImage: `url(${project.img ? project.img : project.folder + "/thumbnail.jpg"})`, + backgroundSize: "cover", + backgroundPosition: "center" }, - mouseover: e => activeProject.value = i, - mouseout: e => activeProject.value = false, - click: e => { - let link = project.link ? project.link : project.folder - window.open(link, "_blank") + h6: { + marginBottom: "0.25em", + fontWeight: "bold", + text: project.title, }, - onready: slideDown - } - }), + p: project.desc, + }, + ul: { + marginTop: "0.2em", + li: project.tags.sort(sortWords).map(tag => { + if (!allTags.value.includes(tag)) allTags.value = [...allTags.value, tag].sort(sortWords); + return { + borderRadius: "0.25em", + padding: "0.2em 0.4em", + marginRight: "0.2em", + backgroundColor: STYLE.COLOR.PALE, + color: STYLE.COLOR.LINK, + border: "solid 1px", + borderColor: activeTag.bind({ + [tag]: STYLE.COLOR.LINK, + default: STYLE.COLOR.PALE, + }), + display: "inline-block", + text: tag, + click: e => { + activeTag.value = activeTag.value === tag ? NONE : tag; + e.stopPropagation(); + } + } + }) + }, + mouseover: e => activeProject.value = i, + mouseout: e => activeProject.value = false, + click: e => { + let link = project.link ? project.link : project.folder + window.open(link, "_blank") + }, + onready: slideDown + })), onready: () => { showTag(); activeTag.value = false; diff --git a/src/style.js b/src/style.js index c4ced45..ce26c9c 100755 --- a/src/style.js +++ b/src/style.js @@ -5,7 +5,6 @@ export const COLOR = { HIGHLIGHT: 'rgb(0, 180, 180)', PAGE: '#F5DC9A', PALE: '#d6cec0', - FADED:'rgba(255,225,200,0.75)' }; export const SHADOW = {