From 6c94ce66a22ba69931fe7b56ac9092d6f85b675b Mon Sep 17 00:00:00 2001 From: yumoqing Date: Thu, 23 Jul 2026 14:57:14 +0800 Subject: [PATCH] add FlipCard and Carousel bricks widgets + CSS --- bricks/build.sh | 2 +- bricks/carousel.js | 75 +++++++++++++++++++++++++++++++++++++++++++ bricks/css/bricks.css | 60 ++++++++++++++++++++++++++++++++++ bricks/flipcard.js | 34 ++++++++++++++++++++ 4 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 bricks/carousel.js create mode 100644 bricks/flipcard.js diff --git a/bricks/build.sh b/bricks/build.sh index 594fe74..4f4d19b 100755 --- a/bricks/build.sh +++ b/bricks/build.sh @@ -12,7 +12,7 @@ SOURCES=" page_data_loader.js factory.js uitypesdef.js utils.js uitype.js \ line.js pie.js bar.js gobang.js period.js iconbarpage.js \ keypress.js asr.js webspeech.js countdown.js progressbar.js \ qaframe.js svg.js videoplayer.js scatter.js radar.js kline.js \ - heatmap.js map.js qr.js textfiles.js agent.js api_doc.js " + heatmap.js map.js qr.js textfiles.js agent.js api_doc.js flipcard.js carousel.js " echo ${SOURCES} cat ${SOURCES} > ../dist/bricks.js # uglifyjs --compress --mangle -- ../dist/bricks.js > ../dist/bricks.min.js diff --git a/bricks/carousel.js b/bricks/carousel.js new file mode 100644 index 0000000..bf20e28 --- /dev/null +++ b/bricks/carousel.js @@ -0,0 +1,75 @@ +// Carousel — auto-rotating FlipCard strip +var bricks = window.bricks || {}; + +bricks.Carousel = class extends bricks.Layout { + /* options: { cards:[{front:{widgettype:...}, back:{widgettype:...}}], cardWidth, cardHeight, interval } */ + constructor(opts) { + super(opts); + this.set_css('bricks-carousel'); + this.set_style('position','relative'); + this.set_style('overflow','hidden'); + this._init(); + } + async _init() { + var cd = this.opts.cards || []; + if (!cd.length) return; + var cw = this.opts.cardWidth || 280; + var ch = this.opts.cardHeight || 360; + var iv = this.opts.interval || 3000; + var sf = this.opts.scaleFactors || [1, 0.85, 0.7, 0.55, 0.4]; + var ox = this.opts.offsets || [0, 220, 390, 500, 600]; + var N = cd.length; + var mid = Math.floor(N / 2); + this._cards = []; + + for (var i = 0; i < N; i++) { + var fc = new bricks.FlipCard({ width: cw + 'px', height: ch + 'px' }); + fc.set_style('position', 'absolute'); + fc.set_style('left', '50%'); + fc.set_style('top', '50%'); + fc.set_style('marginLeft', (-cw / 2) + 'px'); + fc.set_style('marginTop', (-ch / 2) + 'px'); + fc.set_style('transition', 'all .5s cubic-bezier(.4,0,.2,1)'); + fc.set_style('transformOrigin', 'center center'); + fc._pos = i - mid; + + // Build front/back content asynchronously + if (cd[i].front) { + var fw = await bricks.widgetBuild(cd[i].front); + if (fw) fc.front.appendChild(fw.dom_element); + } + if (cd[i].back) { + var bw = await bricks.widgetBuild(cd[i].back); + if (bw) fc.back.appendChild(bw.dom_element); + } + + this._cards.push(fc); + this.dom_element.appendChild(fc.dom_element); + } + + this._sf = sf; this._ox = ox; + this._rotate(0); + this._timer = setInterval(this._rotate.bind(this, 1), iv); + } + _rotate(dir) { + var cards = this._cards, sf = this._sf, ox = this._ox; + if (!cards) return; + cards.forEach(function(c) { + var np = c._pos - dir; + if (np > 4) np = -4; if (np < -4) np = 4; + c._pos = np; + var a = Math.abs(np), idx = Math.min(a, sf.length - 1); + var x = np >= 0 ? ox[idx] : -ox[idx]; + c.set_styles({ + transform: 'translateX(' + x + 'px) scale(' + sf[idx] + ')', + zIndex: String(5 - idx), + opacity: String(idx === 4 ? 0 : sf[idx]), + pointerEvents: a > 3 ? 'none' : 'auto' + }); + }); + } + create() { this.dom_element = document.createElement('div'); } + destroy() { if (this._timer) clearInterval(this._timer); super.destroy(); } +}; + +bricks.Factory.register('Carousel', bricks.Carousel); diff --git a/bricks/css/bricks.css b/bricks/css/bricks.css index 9ec2a27..37e112c 100755 --- a/bricks/css/bricks.css +++ b/bricks/css/bricks.css @@ -912,3 +912,63 @@ hr { height: auto; margin: 0; } + +/* === FlipCard widget === */ +.bricks-flipcard { + perspective: 1000px; + cursor: pointer; +} +.bricks-flipcard-inner { + position: relative; + width: 100%; + height: 100%; + transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1); + transform-style: preserve-3d; +} +.bricks-flipcard:hover .bricks-flipcard-inner { + transform: rotateY(180deg); +} +.bricks-flipcard-front, +.bricks-flipcard-back { + position: absolute; + width: 100%; + height: 100%; + backface-visibility: hidden; + -webkit-backface-visibility: hidden; + top: 0; + left: 0; + border-radius: 16px; + overflow: hidden; +} +.bricks-flipcard-back { + transform: rotateY(180deg); +} +.bricks-flipcard-front { + background: #fff; + box-shadow: 0 18px 54px rgba(90, 60, 160, 0.1), 0 4px 16px rgba(90, 60, 160, 0.05); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 24px; + gap: 12px; +} +.bricks-flipcard-back { + background: #3b82f6; + box-shadow: 0 18px 54px rgba(59, 130, 246, 0.2); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 24px; + gap: 12px; +} + +/* === Carousel widget === */ +.bricks-carousel { + position: relative; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; +} diff --git a/bricks/flipcard.js b/bricks/flipcard.js new file mode 100644 index 0000000..a3778fa --- /dev/null +++ b/bricks/flipcard.js @@ -0,0 +1,34 @@ +// FlipCard — hover to flip, front/back as flat DOM templates +var bricks = window.bricks || {}; + +bricks.FlipCard = class extends bricks.JsWidget { + /* options: { width, height } — front/back styled via CSS child selectors */ + constructor(opts) { + super(opts); + this.set_css('bricks-flipcard'); + var w = this.opts.width || '280px'; + var h = this.opts.height || '360px'; + this.set_style('width', w); + this.set_style('height', h); + this._dom_inner = null; + this._dom_front = null; + this._dom_back = null; + } + create() { + this.dom_element = document.createElement('div'); + this._dom_inner = document.createElement('div'); + this._dom_inner.className = 'bricks-flipcard-inner'; + this._dom_front = document.createElement('div'); + this._dom_front.className = 'bricks-flipcard-front'; + this._dom_back = document.createElement('div'); + this._dom_back.className = 'bricks-flipcard-back'; + this._dom_inner.appendChild(this._dom_front); + this._dom_inner.appendChild(this._dom_back); + this.dom_element.appendChild(this._dom_inner); + } + // Caller builds front/back content into these containers + get front() { return this._dom_front; } + get back() { return this._dom_back; } +}; + +bricks.Factory.register('FlipCard', bricks.FlipCard);