summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreltrac <hi@eltr.ac>2026-11-09 15:28:18 +0800
committereltrac <hi@eltr.ac>2026-11-09 15:28:18 +0800
commitdd2c8caaa8ac62e726ba0ddef08942519fd64037 (patch)
tree575188150c1e276edbdd9499df4c0db5975c5c5a
parent6af8666d1ef3c2a3ab2d212b41503efb0de18d61 (diff)
js: 使用 libra.js 实现图片灯箱HEADmaster
-rw-r--r--README.md2
-rw-r--r--assets/css/libra.css43
-rw-r--r--assets/js/libra.min.js2
-rw-r--r--includes/footer.php7
-rw-r--r--includes/head.php2
5 files changed, 54 insertions, 2 deletions
diff --git a/README.md b/README.md
index 512c068..c0296d2 100644
--- a/README.md
+++ b/README.md
@@ -83,10 +83,10 @@ $ git clone https://github.com/BigCoke233/miracles.git
- [Pangu.js](https://github.com/vinta/pangu.js) -分割中英文字符
- [Normalize.css](http://necolas.github.io/normalize.css/) - 保证不同浏览器上各元素 css 默认效果相同
- [qrcode.js](https://github.com/davidshimjs/qrcodejs) - 生成文章二维码
-- [gazeimg](https://github.com/ganxiaozhe/gazeimg) - 图片懒加载 & 灯箱
- [details-element-polyfill](https://github.com/javan/details-element-polyfill) - 优化 details 标签兼容性
- [hint.css](https://kushagra.dev/lab/hint/) - 工具提示 Tooltip
- [toaster.js](https://github.com/BigCoke233/toaster.js/) - Toast 提示框
+- [libra.js](https://codeberg.org/eltrac/libra) - 图片灯箱
- [TableContents](https://github.com/lutdev/table-contents) - 生成文章目录
### 贡献者
diff --git a/assets/css/libra.css b/assets/css/libra.css
new file mode 100644
index 0000000..84216ee
--- /dev/null
+++ b/assets/css/libra.css
@@ -0,0 +1,43 @@
+.libra-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ padding: 1rem;
+ transition: opacity ease-in-out;
+
+ backdrop-filter: blur(2rem);
+ background-color: rgba(255, 255, 255, 0.75);
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.libra-overlay.libra-visible {
+ opacity: 1;
+ z-index: 900;
+}
+
+.libra-overlay.libra-hidden {
+ opacity: 0;
+ z-index: -100;
+}
+
+@media (prefers-color-scheme: dark) {
+ .libra-overlay {
+ background-color: rgba(0, 0, 0, 0.75);
+ }
+}
+
+.libra-shadow {
+ position: absolute;
+ transition-property: transform;
+ z-index: 1000;
+}
+
+.libra-shadow.open {
+ box-shadow: 0 0 1rem rgba(0, 0, 0, 0.5);
+}
+
diff --git a/assets/js/libra.min.js b/assets/js/libra.min.js
new file mode 100644
index 0000000..22b54b9
--- /dev/null
+++ b/assets/js/libra.min.js
@@ -0,0 +1,2 @@
+// https://codeberg.org/eltrac/libra/src/branch/master/dist/libra.min.js 2d5c1451b0
+let t={selector:"[data-libra]",margin:50,zeroMarginBreakpoint:768,offset:{x:0,y:0},transitionDuration:200,imageCursor:"zoom-in"};class e{constructor(){let e=document.createElement("div");return e.id="libra-overlay",e.classList.add("libra-overlay","libra-hidden"),e.style.transitionDuration=t.transitionDuration+"ms",this.element=e,document.body.appendChild(e),this}show(){this.element.classList.remove("libra-hidden"),this.element.classList.add("libra-visible")}hide(){this.element.classList.remove("libra-visible"),this.element.classList.add("libra-hidden")}}class i{static calculateFinalState(e){let i={},s=t.margin;window.innerWidth<=t.zeroMarginBreakpoint&&(s=0);let n=e.naturalWidth,o=e.naturalHeight,l=n/o,r=window.innerWidth-2*s,a=window.innerHeight-2*s;return i.width=n>r?r:n,i.height=n>r?r/l:o,i.height>a&&(i.height=a,i.width=a*l),i.top=(a-i.height)/2+window.scrollY+s+t.offset.y,i.left=(r-i.width)/2+window.scrollX+s+t.offset.x,i}static transformMatrix(t,e,i){let s=i.width/e.width,n=i.height/e.height,o=e.left+e.width/2,l=e.top+e.height/2,r=i.left+i.width/2,a=i.top+i.height/2;t.style.transform=`matrix(${s}, 0, 0, ${n}, ${r-o}, ${a-l})`}static resetTransformMatrix(t){t.style.transform="matrix(1, 0, 0, 1, 0, 0)"}}class s{isOpen=!1;constructor(t){return this.original=t,this.fetchOriginalPosition(),this.create(t),this}create(e){let i=document.createElement("img");i.src=e.src,i.id=`libra-shadow-${e.id}`,i.classList.add("libra-shadow"),i.style.transitionDuration=t.transitionDuration+"ms",this.element=i,this.positionShadow(),this.placeItself()}fetchOriginalPosition(){let e=this.original.getBoundingClientRect();this.originalPosition={top:e.top+window.scrollY+t.offset.y,left:e.left+window.scrollX+t.offset.x,width:e.width,height:e.height}}positionShadow(){this.element.style.top=this.originalPosition.top+"px",this.element.style.left=this.originalPosition.left+"px",this.element.style.width=this.originalPosition.width+"px",this.element.style.height=this.originalPosition.height+"px"}open(){this.isOpen=!0,this.element.classList.add("open"),this.fetchOriginalPosition(),this.positionShadow(),this.original.style.visibility="hidden",document.body.contains(this.element)||this.placeItself();let t=()=>i.transformMatrix(this.element,this.originalPosition,i.calculateFinalState(this.element));0===this.element.naturalWidth||0===this.element.naturalHeight?this.element.addEventListener("load",t,{once:!0}):t()}close(){this.isOpen=!1,this.element.classList.remove("open"),i.resetTransformMatrix(this.element),setTimeout(()=>{this.original.style.visibility="visible",this.destroyItself()},t.transitionDuration)}placeItself(){document.body.appendChild(this.element)}destroyItself(){document.body.contains(this.element)&&document.body.removeChild(this.element)}}class n{currentImage=null;shadows={};constructor(e){!function t(e,i){for(let s in i)"object"!=typeof i[s]||null===i[s]||Array.isArray(i[s])?e[s]=i[s]:(e[s]||(e[s]={}),t(e[s],i[s]));return e}(t,e),this.initOverlay(),this.initImages(),this.initControls()}initOverlay(){this.overlay=new e,this.overlay.element.addEventListener("click",()=>{this.closeCurrent()})}initImages(){this.images=document.querySelectorAll(t.selector),this.images.forEach(e=>{"IMG"===e.tagName&&(e.id=crypto.randomUUID(),e.style.cursor=t.imageCursor,e.addEventListener("click",()=>this.open(e)))})}initControls(){document.addEventListener("keydown",t=>{("Escape"===t.key||" "===t.key)&&this.closeCurrent()}),document.body.addEventListener("wheel",t=>this.closeCurrent()),document.body.addEventListener("touchmove",t=>this.closeCurrent())}open(t){this.currentImage=t;let e=this.shadows[t.id]||new s(t);this.shadows[t.id]||(this.shadows[t.id]=e),e.open(),this.overlay.show()}close(t){this.currentImage=null,this.shadows[t.id]&&(this.shadows[t.id].close(),this.overlay.hide())}closeCurrent(){this.currentImage&&this.close(this.currentImage)}}export{n as default};
diff --git a/includes/footer.php b/includes/footer.php
index 294140b..a4e56aa 100644
--- a/includes/footer.php
+++ b/includes/footer.php
@@ -53,6 +53,13 @@
if($this->options->customCDN): $custom=$this->options->customCDN; else: $custom=Helper::options()->themeUrl("","Miracles"); endif;
Utils::addRequires($js_files,"js",$this->options->CDN,$custom);
?>
+
+ <!-- Load libra.js (lightbox) -->
+ <script type="module">
+ import LightBox from '<?php Utils::indexTheme('assets/js/libra.min.js') ?>';
+ new LightBox();
+ </script>
+
<!-- JavaScript -->
<!--<nocompress>--><script>
<?php if($this->options->build_time)echo "startTime(\"".$this->options->build_time."\");" ?>
diff --git a/includes/head.php b/includes/head.php
index b1b4499..eb6431e 100644
--- a/includes/head.php
+++ b/includes/head.php
@@ -45,7 +45,7 @@ if($GLOBALS['miraclesIfCompressHTML']=='on') ob_start(); //ob 截取开始 ?>
<?php $this->options->headerEcho(); ?>
<!-- css -->
<?php
- $css_files=array("codestyle","owo.min","nprogress","hint.min","main/miracles.min","toaster");
+ $css_files=array("codestyle","owo.min","nprogress","hint.min","main/miracles.min","toaster","libra");
if($this->options->customCDN): $custom=$this->options->customCDN; else: $custom=Helper::options()->themeUrl("","Miracles"); endif;
Utils::addRequires($css_files,"css",$this->options->CDN,$custom);
?>