Browse Source

2024.03.29

格雷 1 year ago
parent
commit
21e0ed57c7

+ 16 - 18
.idea/workspace.xml

@@ -1,23 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="ChangeListManager">
-    <list default="true" id="0c4e5391-a61c-4580-a848-8f7f11272093" name="更改" comment="2024.03.27">
+    <list default="true" id="0c4e5391-a61c-4580-a848-8f7f11272093" name="更改" comment="2024.03.28">
+      <change afterPath="$PROJECT_DIR$/static/js/fish.js" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/static/js/quietflow.min.js" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/templates/iframe-comic-details.html" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/app.py" beforeDir="false" afterPath="$PROJECT_DIR$/app.py" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/static/img/LOGO.svg" beforeDir="false" afterPath="$PROJECT_DIR$/static/img/LOGO.svg" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/static/js/Sortable/Sortable.js" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/static/js/Sortable/Sortable.min.js" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/static/js/layui-modules/tag.css" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/static/js/layui-modules/tag.js" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/static/js/layui-v2.9.7/css/layui.css" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/static/js/layui-v2.9.7/font/iconfont.eot" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/static/js/layui-v2.9.7/font/iconfont.svg" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/static/js/layui-v2.9.7/font/iconfont.ttf" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/static/js/layui-v2.9.7/font/iconfont.woff" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/static/js/layui-v2.9.7/font/iconfont.woff2" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/static/js/layui-v2.9.7/layui.js" beforeDir="false" />
       <change beforePath="$PROJECT_DIR$/templates/base-layout.html" beforeDir="false" afterPath="$PROJECT_DIR$/templates/base-layout.html" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/templates/iframe-search-results.html" beforeDir="false" afterPath="$PROJECT_DIR$/templates/iframe-search-results.html" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/templates/search-container.html" beforeDir="false" afterPath="$PROJECT_DIR$/templates/search-container.html" afterDir="false" />
     </list>
     <option name="SHOW_DIALOG" value="false" />
@@ -132,7 +122,7 @@
       <workItem from="1710318758218" duration="425000" />
       <workItem from="1710319189675" duration="6270000" />
       <workItem from="1711329272552" duration="18425000" />
-      <workItem from="1711414296788" duration="48525000" />
+      <workItem from="1711414296788" duration="65308000" />
     </task>
     <task id="LOCAL-00001" summary="2024.03.13">
       <created>1710318899380</created>
@@ -197,7 +187,14 @@
       <option name="project" value="LOCAL" />
       <updated>1711537238782</updated>
     </task>
-    <option name="localTasksCounter" value="10" />
+    <task id="LOCAL-00010" summary="2024.03.28">
+      <created>1711626422295</created>
+      <option name="number" value="00010" />
+      <option name="presentableId" value="LOCAL-00010" />
+      <option name="project" value="LOCAL" />
+      <updated>1711626422295</updated>
+    </task>
+    <option name="localTasksCounter" value="11" />
     <servers />
   </component>
   <component name="TypeScriptGeneratedFilesManager">
@@ -219,10 +216,11 @@
     <MESSAGE value="2024.03.25" />
     <MESSAGE value="2024.03.26" />
     <MESSAGE value="2024.03.27" />
-    <option name="LAST_COMMIT_MESSAGE" value="2024.03.27" />
+    <MESSAGE value="2024.03.28" />
+    <option name="LAST_COMMIT_MESSAGE" value="2024.03.28" />
   </component>
   <component name="com.intellij.coverage.CoverageDataManagerImpl">
     <SUITE FILE_PATH="coverage/IOmga$Flask__app_py_.coverage" NAME="Flask (app.py) 覆盖结果" MODIFIED="1710318377612" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
-    <SUITE FILE_PATH="coverage/IOmga$app.coverage" NAME="app 覆盖结果" MODIFIED="1711622316053" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
+    <SUITE FILE_PATH="coverage/IOmga$app.coverage" NAME="app 覆盖结果" MODIFIED="1711699260150" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
   </component>
 </project>

+ 7 - 0
app.py

@@ -45,6 +45,13 @@ def search_page():
     return render_template("iframe-search-results.html", render_data=data)
 
 
+@server.route("/ComicDetails/<platform>/<comic>")
+def comic_details_page(platform, comic):
+    print(platform, comic)
+    data = {"comic": comic}
+    return render_template("iframe-comic-details.html", render_data=data)
+
+
 if __name__ == '__main__':
     chinese = {
         'global.quitConfirmation': u'确定关闭?',

+ 334 - 0
static/js/fish.js

@@ -0,0 +1,334 @@
+var RENDERER = {
+    POINT_INTERVAL: 5,
+    FISH_COUNT: 3,
+    MAX_INTERVAL_COUNT: 50,
+    INIT_HEIGHT_RATE: 0.5,
+    THRESHOLD: 50,
+
+    init: function () {
+        this.setParameters();
+        this.reconstructMethods();
+        this.setup();
+        this.bindEvent();
+        this.render();
+    },
+    setParameters: function () {
+        this.$window = $(window);
+        this.$container = $('#jsi-flying-fish-container');
+        this.$canvas = $('<canvas />');
+        this.context = this.$canvas.appendTo(this.$container).get(0).getContext('2d');
+        this.points = [];
+        this.fishes = [];
+        this.watchIds = [];
+    },
+    createSurfacePoints: function () {
+        var count = Math.round(this.width / this.POINT_INTERVAL);
+        this.pointInterval = this.width / (count - 1);
+        this.points.push(new SURFACE_POINT(this, 0));
+
+        for (var i = 1; i < count; i++) {
+            var point = new SURFACE_POINT(this, i * this.pointInterval),
+                previous = this.points[i - 1];
+
+            point.setPreviousPoint(previous);
+            previous.setNextPoint(point);
+            this.points.push(point);
+        }
+    },
+    reconstructMethods: function () {
+        this.watchWindowSize = this.watchWindowSize.bind(this);
+        this.jdugeToStopResize = this.jdugeToStopResize.bind(this);
+        this.startEpicenter = this.startEpicenter.bind(this);
+        this.moveEpicenter = this.moveEpicenter.bind(this);
+        this.reverseVertical = this.reverseVertical.bind(this);
+        this.render = this.render.bind(this);
+    },
+    setup: function () {
+        this.points.length = 0;
+        this.fishes.length = 0;
+        this.watchIds.length = 0;
+        this.intervalCount = this.MAX_INTERVAL_COUNT;
+        this.width = this.$container.width();
+        this.height = this.$container.height();
+        this.fishCount = this.FISH_COUNT * this.width / 500 * this.height / 500;
+        this.$canvas.attr({width: this.width, height: this.height});
+        this.reverse = false;
+
+        this.fishes.push(new FISH(this));
+        this.createSurfacePoints();
+    },
+    watchWindowSize: function () {
+        this.clearTimer();
+        this.tmpWidth = this.$window.width();
+        this.tmpHeight = this.$window.height();
+        this.watchIds.push(setTimeout(this.jdugeToStopResize, this.WATCH_INTERVAL));
+    },
+    clearTimer: function () {
+        while (this.watchIds.length > 0) {
+            clearTimeout(this.watchIds.pop());
+        }
+    },
+    jdugeToStopResize: function () {
+        var width = this.$window.width(),
+            height = this.$window.height(),
+            stopped = (width == this.tmpWidth && height == this.tmpHeight);
+
+        this.tmpWidth = width;
+        this.tmpHeight = height;
+
+        if (stopped) {
+            this.setup();
+        }
+    },
+    bindEvent: function () {
+        this.$window.on('resize', this.watchWindowSize);
+        this.$container.on('mouseenter', this.startEpicenter);
+        this.$container.on('mousemove', this.moveEpicenter);
+        this.$container.on('click', this.reverseVertical);
+    },
+    getAxis: function (event) {
+        var offset = this.$container.offset();
+
+        return {
+            x: event.clientX - offset.left + this.$window.scrollLeft(),
+            y: event.clientY - offset.top + this.$window.scrollTop()
+        };
+    },
+    startEpicenter: function (event) {
+        this.axis = this.getAxis(event);
+    },
+    moveEpicenter: function (event) {
+        var axis = this.getAxis(event);
+
+        if (!this.axis) {
+            this.axis = axis;
+        }
+        this.generateEpicenter(axis.x, axis.y, axis.y - this.axis.y);
+        this.axis = axis;
+    },
+    generateEpicenter: function (x, y, velocity) {
+        if (y < this.height / 2 - this.THRESHOLD || y > this.height / 2 + this.THRESHOLD) {
+            return;
+        }
+        var index = Math.round(x / this.pointInterval);
+
+        if (index < 0 || index >= this.points.length) {
+            return;
+        }
+        this.points[index].interfere(y, velocity);
+    },
+    reverseVertical: function () {
+        this.reverse = !this.reverse;
+
+        for (var i = 0, count = this.fishes.length; i < count; i++) {
+            this.fishes[i].reverseVertical();
+        }
+    },
+    controlStatus: function () {
+        for (var i = 0, count = this.points.length; i < count; i++) {
+            this.points[i].updateSelf();
+        }
+        for (var i = 0, count = this.points.length; i < count; i++) {
+            this.points[i].updateNeighbors();
+        }
+        if (this.fishes.length < this.fishCount) {
+            if (--this.intervalCount == 0) {
+                this.intervalCount = this.MAX_INTERVAL_COUNT;
+                this.fishes.push(new FISH(this));
+            }
+        }
+    },
+    render: function () {
+        requestAnimationFrame(this.render);
+        this.controlStatus();
+        this.context.clearRect(0, 0, this.width, this.height);
+        this.context.fillStyle = 'hsl(0, 0%, 95%)';
+
+        for (var i = 0, count = this.fishes.length; i < count; i++) {
+            this.fishes[i].render(this.context);
+        }
+        this.context.save();
+        this.context.globalCompositeOperation = 'xor';
+        this.context.beginPath();
+        this.context.moveTo(0, this.reverse ? 0 : this.height);
+
+        for (var i = 0, count = this.points.length; i < count; i++) {
+            this.points[i].render(this.context);
+        }
+        this.context.lineTo(this.width, this.reverse ? 0 : this.height);
+        this.context.closePath();
+        this.context.fill();
+        this.context.restore();
+    }
+};
+var SURFACE_POINT = function (renderer, x) {
+    this.renderer = renderer;
+    this.x = x;
+    this.init();
+};
+SURFACE_POINT.prototype = {
+    SPRING_CONSTANT: 0.03,
+    SPRING_FRICTION: 0.9,
+    WAVE_SPREAD: 0.3,
+    ACCELARATION_RATE: 0.01,
+
+    init: function () {
+        this.initHeight = this.renderer.height * this.renderer.INIT_HEIGHT_RATE;
+        this.height = this.initHeight;
+        this.fy = 0;
+        this.force = {previous: 0, next: 0};
+    },
+    setPreviousPoint: function (previous) {
+        this.previous = previous;
+    },
+    setNextPoint: function (next) {
+        this.next = next;
+    },
+    interfere: function (y, velocity) {
+        this.fy = this.renderer.height * this.ACCELARATION_RATE * ((this.renderer.height - this.height - y) >= 0 ? -1 : 1) * Math.abs(velocity);
+    },
+    updateSelf: function () {
+        this.fy += this.SPRING_CONSTANT * (this.initHeight - this.height);
+        this.fy *= this.SPRING_FRICTION;
+        this.height += this.fy;
+    },
+    updateNeighbors: function () {
+        if (this.previous) {
+            this.force.previous = this.WAVE_SPREAD * (this.height - this.previous.height);
+        }
+        if (this.next) {
+            this.force.next = this.WAVE_SPREAD * (this.height - this.next.height);
+        }
+    },
+    render: function (context) {
+        if (this.previous) {
+            this.previous.height += this.force.previous;
+            this.previous.fy += this.force.previous;
+        }
+        if (this.next) {
+            this.next.height += this.force.next;
+            this.next.fy += this.force.next;
+        }
+        context.lineTo(this.x, this.renderer.height - this.height);
+    }
+};
+var FISH = function (renderer) {
+    this.renderer = renderer;
+    this.init();
+};
+FISH.prototype = {
+    GRAVITY: 0.4,
+
+    init: function () {
+        this.direction = Math.random() < 0.5;
+        this.x = this.direction ? (this.renderer.width + this.renderer.THRESHOLD) : -this.renderer.THRESHOLD;
+        this.previousY = this.y;
+        this.vx = this.getRandomValue(4, 10) * (this.direction ? -1 : 1);
+
+        if (this.renderer.reverse) {
+            this.y = this.getRandomValue(this.renderer.height * 1 / 10, this.renderer.height * 4 / 10);
+            this.vy = this.getRandomValue(2, 5);
+            this.ay = this.getRandomValue(0.05, 0.2);
+        } else {
+            this.y = this.getRandomValue(this.renderer.height * 6 / 10, this.renderer.height * 9 / 10);
+            this.vy = this.getRandomValue(-5, -2);
+            this.ay = this.getRandomValue(-0.2, -0.05);
+        }
+        this.isOut = false;
+        this.theta = 0;
+        this.phi = 0;
+    },
+    getRandomValue: function (min, max) {
+        return min + (max - min) * Math.random();
+    },
+    reverseVertical: function () {
+        this.isOut = !this.isOut;
+        this.ay *= -1;
+    },
+    controlStatus: function (context) {
+        this.previousY = this.y;
+        this.x += this.vx;
+        this.y += this.vy;
+        this.vy += this.ay;
+
+        if (this.renderer.reverse) {
+            if (this.y > this.renderer.height * this.renderer.INIT_HEIGHT_RATE) {
+                this.vy -= this.GRAVITY;
+                this.isOut = true;
+            } else {
+                if (this.isOut) {
+                    this.ay = this.getRandomValue(0.05, 0.2);
+                }
+                this.isOut = false;
+            }
+        } else {
+            if (this.y < this.renderer.height * this.renderer.INIT_HEIGHT_RATE) {
+                this.vy += this.GRAVITY;
+                this.isOut = true;
+            } else {
+                if (this.isOut) {
+                    this.ay = this.getRandomValue(-0.2, -0.05);
+                }
+                this.isOut = false;
+            }
+        }
+        if (!this.isOut) {
+            this.theta += Math.PI / 20;
+            this.theta %= Math.PI * 2;
+            this.phi += Math.PI / 30;
+            this.phi %= Math.PI * 2;
+        }
+        this.renderer.generateEpicenter(this.x + (this.direction ? -1 : 1) * this.renderer.THRESHOLD, this.y, this.y - this.previousY);
+
+        if (this.vx > 0 && this.x > this.renderer.width + this.renderer.THRESHOLD || this.vx < 0 && this.x < -this.renderer.THRESHOLD) {
+            this.init();
+        }
+    },
+    render: function (context) {
+        context.save();
+        context.translate(this.x, this.y);
+        context.rotate(Math.PI + Math.atan2(this.vy, this.vx));
+        context.scale(1, this.direction ? 1 : -1);
+        context.beginPath();
+        context.moveTo(-30, 0);
+        context.bezierCurveTo(-20, 15, 15, 10, 40, 0);
+        context.bezierCurveTo(15, -10, -20, -15, -30, 0);
+        context.fill();
+
+        context.save();
+        context.translate(40, 0);
+        context.scale(0.9 + 0.2 * Math.sin(this.theta), 1);
+        context.beginPath();
+        context.moveTo(0, 0);
+        context.quadraticCurveTo(5, 10, 20, 8);
+        context.quadraticCurveTo(12, 5, 10, 0);
+        context.quadraticCurveTo(12, -5, 20, -8);
+        context.quadraticCurveTo(5, -10, 0, 0);
+        context.fill();
+        context.restore();
+
+        context.save();
+        context.translate(-3, 0);
+        context.rotate((Math.PI / 3 + Math.PI / 10 * Math.sin(this.phi)) * (this.renderer.reverse ? -1 : 1));
+
+        context.beginPath();
+
+        if (this.renderer.reverse) {
+            context.moveTo(5, 0);
+            context.bezierCurveTo(10, 10, 10, 30, 0, 40);
+            context.bezierCurveTo(-12, 25, -8, 10, 0, 0);
+        } else {
+            context.moveTo(-5, 0);
+            context.bezierCurveTo(-10, -10, -10, -30, 0, -40);
+            context.bezierCurveTo(12, -25, 8, -10, 0, 0);
+        }
+        context.closePath();
+        context.fill();
+        context.restore();
+        context.restore();
+        this.controlStatus(context);
+    }
+};
+$(function () {
+    RENDERER.init();
+});

+ 158 - 0
static/js/quietflow.min.js

@@ -0,0 +1,158 @@
+/**
+ * quietflow.min.js
+ * Paul Krishnamurthy 2016
+ *
+ * https://paulkr.com
+ * paul@paulkr.com
+ */
+
+function randCol(a, o, e, r) {
+    return "rgba(" + Math.floor(Math.random() * a).toString() + "," + Math.floor(Math.random() * o).toString() + "," + Math.floor(Math.random() * e).toString() + "," + r + ")"
+}
+
+$.fn.quietflow = function (a) {
+    function o(a) {
+        void 0 !== x.speed ? setTimeout(function () {
+            w = requestAnimationFrame(a)
+        }, x.speed) : w = requestAnimationFrame(a)
+    }
+
+    function e() {
+        for (var a = 0; h > a; a += x.squareSize + 1) for (var r = 0; c > r; r += x.squareSize + 1) S.fillStyle = randCol(x.maxRed, x.maxGreen, x.maxBlue, 1), S.fillRect(a, r, x.squareSize, x.squareSize);
+        o(e)
+    }
+
+    function r() {
+        (B + y > h || 0 > B + y) && (y = -y), (F + k > c || 0 > F + k) && (k = -k), B += y, F += k, S.fillStyle = x.backgroundCol, S.fillRect(0, 0, h, c);
+        for (var a = 0; a < x.miniRadii; a++) for (var e = 0; e < x.miniRadii; e++) {
+            var t = a / x.miniRadii * h, l = e / x.miniRadii * c,
+                i = Math.sqrt(Math.pow(B - t, 2) + Math.pow(F - l, 2)) / x.mainRadius;
+            S.beginPath(), S.fillStyle = x.circleCol, S.arc(t, l, i, 0, 2 * Math.PI, !0), S.closePath(), S.fill()
+        }
+        o(r)
+    }
+
+    function t() {
+        S.fillStyle = x.backgroundCol, S.fillRect(0, 0, h, c);
+        for (var a = 0; a < x.bounceBallCount; a++) {
+            var e = circleData[a], r = 0, l = 1, i = 2, n = 3, s = 4, d = 5;
+            (e[r] + e[n] > h || e[r] + e[n] < 0) && (e[3] = -e[3]), (e[l] + e[s] > c || e[l] + e[s] < 0) && (e[s] = -e[s]), e[r] += e[n], e[l] += e[s], S.beginPath(), S.fillStyle = e[d], S.arc(e[r], e[l], e[i], 0, 2 * Math.PI, !0), S.closePath(), S.fill()
+        }
+        o(t)
+    }
+
+    function l() {
+        S.fillStyle = x.backgroundCol, S.fillRect(0, 0, h, c), S.beginPath(), S.fillStyle = x.lineColor, S.arc(f, m, 2, 0, 2 * Math.PI, !0), S.closePath(), S.fill();
+        for (var a = 0; a < x.lines; a++) S.beginPath(), S.moveTo(f, m), S.lineTo(Math.random() * h, Math.random() * c), S.strokeStyle = x.lineColor, S.shadowColor = x.lineGlow, S.shadowBlur = 20, S.stroke();
+        o(l)
+    }
+
+    function i() {
+        var a = S.createLinearGradient(0, 0, h / 2, c);
+        a.addColorStop(0, "#333333"), a.addColorStop(1, "#000"), S.fillStyle = a, S.fillRect(0, 0, h, c);
+        for (var e = 0; e < P.length; e++) {
+            var r = P[e], t = 0, l = 1, n = 2, s = 3;
+            r[t] += r[s], S.beginPath(), S.fillStyle = x.starColor, S.arc(r[t], r[l], r[n], 0, 2 * Math.PI, !0), S.shadowColor = "#FFF", S.shadowBlur = 20, S.closePath(), S.fill(), r[t] > h && (P.splice(e, 1), P.unshift([Math.random() * h / 4 - h / 4, Math.random() * c, Math.random() * x.starSize, Math.ceil(5 * Math.random())]))
+        }
+        o(i)
+    }
+
+    function n() {
+        S.beginPath();
+        for (var a = [[0, 0], [h, 0], [0, c], [h, c]], e = 0; 4 > e; e++) {
+            var r = Math.floor(Math.random() * x.specificColors.length);
+            S.strokeStyle = x.specificColors.length > 0 ? x.specificColors[r] : randCol(255, 255, 255), S.moveTo(a[e][0], a[e][1]), S.lineTo(Math.random() * h, Math.random() * c)
+        }
+        S.shadowColor = x.lineGlow, S.shadowBlur = 20, S.stroke(), o(n)
+    }
+
+    function s() {
+        S.fillStyle = x.backgroundCol, S.fillRect(0, 0, h, c);
+        for (var a = 0; a < q.length; a++) {
+            var e = q[a], r = 0, t = 1, l = 2, i = 3, n = 4;
+            S.fillStyle = e[i], S.fillRect(e[r], e[t], e[l], e[l]), e[r] += e[n], e[t] -= e[n], (e[r] > h + x.maxBoxSize || e[t] < -x.maxBoxSize) && (q.splice(a, 1), 0 == x.specificColors.length ? q.push([Math.random() * h * 2 - h, Math.random() * c * 2 + c, Math.random() * x.maxBoxSize + 1, randCol(255, 255, 255, x.transparent ? .5 : 1), 5 * Math.random()]) : q.push([Math.random() * h * 2 - h, Math.random() * c * 2 + c, Math.random() * x.maxBoxSize + 1, x.specificColors[Math.floor(Math.random() * x.specificColors.length)], 5 * Math.random()]))
+        }
+        o(s)
+    }
+
+    var d = $(this), h = d.width(), c = d.height(), f = h / 2, m = c / 2;
+    $("#Quietflow").remove();
+    var u = "starfield", M = -1e3,
+        p = ["squareFlash", "vortex", "bouncingBalls", "shootingLines", "simpleGradient", "starfield", "layeredTriangles", "cornerSpikes", "floatingBoxes"],
+        C = {
+            squareFlash: {squareSize: 10, maxRed: 255, maxGreen: 255, maxBlue: 255, speed: 100},
+            vortex: {mainRadius: 20, miniRadii: 30, backgroundCol: "#3498DB", circleCol: "#34495E", speed: 10},
+            bouncingBalls: {
+                specificColors: [],
+                backgroundCol: "#ECF0F1",
+                maxRadius: 40,
+                bounceSpeed: 50,
+                bounceBallCount: 50,
+                transparent: !0
+            },
+            shootingLines: {backgroundCol: "#000", lineColor: "#FFF", speed: 150, lineGlow: "#FFF", lines: 50},
+            simpleGradient: {primary: "#D4145A", accent: "#FBB03B"},
+            starfield: {starColor: "#FFF", starSize: 3, speed: 100},
+            layeredTriangles: {backgroundCol: "#D6D6D6", transparent: !0, specificColors: [], triangles: 50},
+            cornerSpikes: {specificColors: [], backgroundCol: "#FFF", lineColor: "#000", speed: 100, lineGlow: "#FFF"},
+            floatingBoxes: {
+                specificColors: [],
+                boxCount: 400,
+                maxBoxSize: 80,
+                backgroundCol: "#D6D6D6",
+                transparent: !1,
+                speed: 100
+            }
+        }, g = document.createElement("canvas"), S = g.getContext("2d");
+    g.id = "Quietflow", g.width = h, g.height = c, g.style.zIndex = M, g.style.position = "absolute", g.style.top = 0;
+    var b = d.attr("id");
+    if (void 0 != b) {
+        var v = document.getElementById(b);
+        v.appendChild(g)
+    } else document.body.appendChild(g);
+    $.inArray(a.theme, p) > -1 && (u = a.theme);
+    var x = {};
+    x = $.extend(C[u], a), $(window).resize(function () {
+        h = d.width(), c = d.height();
+        var a = $("#Quietflow").css("width").replace("px", ""), o = $("#Quietflow").css("height").replace("px", "");
+        $("#Quietflow").css({width: window.innerWidth, height: window.innerHeight});
+        var e = a / window.innerWidth, r = o / window.innerHeight;
+        S.scale(e, r)
+    });
+    var w;
+    switch (u) {
+        case"squareFlash":
+            e();
+            break;
+        case"vortex":
+            var y = 2, k = 4, B = h / 2, F = c / 2;
+            r();
+            break;
+        case"bouncingBalls":
+            circleData = [];
+            for (var R = 0; R < x.bounceBallCount; R++) 0 == x.specificColors.length ? circleData.push([Math.random() * h, Math.random() * c, Math.random() * x.maxRadius, 2 * Math.random(), 4 * Math.random(), randCol(255, 255, 255, x.transparent ? .5 : 1)]) : circleData.push([Math.random() * h, Math.random() * c, Math.random() * x.maxRadius, 2 * Math.random(), 4 * Math.random(), x.specificColors[Math.floor(Math.random() * x.specificColors.length)]]);
+            t();
+            break;
+        case"shootingLines":
+            l();
+            break;
+        case"simpleGradient":
+            var z = S.createLinearGradient(0, 0, h / 2, c);
+            z.addColorStop(0, x.primary), z.addColorStop(1, x.accent), S.fillStyle = z, S.fillRect(0, 0, h, c);
+            break;
+        case"starfield":
+            for (var P = [], R = 0; 700 > R; R++) P.push([Math.random() * h * 2 - h, Math.random() * c, Math.random() * x.starSize, Math.ceil(5 * Math.random())]);
+            i();
+            break;
+        case"layeredTriangles":
+            S.fillStyle = x.backgroundCol, S.fillRect(0, 0, h, c);
+            for (var R = 0; R < x.triangles; R++) S.beginPath(), S.moveTo(Math.random() * h, Math.random() * c), S.lineTo(Math.random() * h, Math.random() * c), S.lineTo(Math.random() * h, Math.random() * c), x.specificColors.length > 0 ? S.fillStyle = x.specificColors[Math.floor(Math.random() * x.specificColors.length)] : S.fillStyle = randCol(255, 255, 255, .5), S.closePath(), S.fill();
+            break;
+        case"cornerSpikes":
+            S.fillStyle = x.backgroundCol, S.fillRect(0, 0, h, c), n();
+            break;
+        case"floatingBoxes":
+            for (var q = [], R = 0; R < x.boxCount; R++) 0 == x.specificColors.length ? q.push([Math.random() * h * 2 - h, Math.random() * c, Math.random() * x.maxBoxSize + 1, randCol(255, 255, 255, x.transparent ? .5 : 1), 5 * Math.random()]) : q.push([Math.random() * h * 2 - h, Math.random() * c, Math.random() * x.maxBoxSize + 1, x.specificColors[Math.floor(Math.random() * x.specificColors.length)], 5 * Math.random()]);
+            s()
+    }
+};

+ 45 - 3
templates/base-layout.html

@@ -51,8 +51,8 @@
         }
 
         .search-body-container {
-            height: 100%;
             width: 100%;
+            height: 100%;
             display: flex;
             align-items: center;
             justify-content: center;
@@ -84,6 +84,40 @@
                 background-position: 0% 50%;
             }
         }
+
+        .fish-gradient {
+            background: -webkit-linear-gradient(left,rgba(89,114,192,0.8),rgba(89,114,192,0.2));
+			background: -o-linear-gradient(right,rgba(89,114,192,0.8),rgba(89,114,192,0.2));
+			background: -moz-linear-gradient(right,rgba(89,114,192,0.8),rgba(89,114,192,0.2));
+			background: linear-gradient(to right, rgba(89,114,192,0.8), rgba(89,114,192,0.2));
+			background-size: 400% 400%;
+			animation: fish-gradientBG 5s ease infinite;
+        }
+
+        @keyframes fish-gradientBG {
+            0% {
+                background-position: 0% 50%;
+            }
+            50% {
+                background-position: 100% 50%;
+            }
+            100% {
+                background-position: 0% 50%;
+            }
+        }
+
+        /* 定义底部承载游动小鱼特效的div样式 */
+        .fish-container {
+            margin:0;
+			padding:0;
+			background-color:transparent;
+			width:100%;
+			height:200px;
+			/* z-index:-1; */
+			position:absolute;
+			bottom:4px;
+			left:0;
+        }
     </style>
 </head>
 <body>
@@ -128,7 +162,8 @@
 
 <!-- 引入 layui.js 的 <script> 标签最好放置在 html 末尾 -->
 <script src="{{ url_for('static', filename='layui-v2.9.7/layui.js') }}"></script>
-<script src="{{ url_for('static', filename='js/Sortable.min.js') }}"></script>
+<script src="{{ url_for('static', filename='js/jquery-3.7.1.min.js') }}"></script>
+<script src="{{ url_for('static', filename='js/fish.js') }}"></script>
 <script>
     layui.config({
         base: "{{ url_for('static', filename='layui-modules/') }}",
@@ -139,13 +174,20 @@
             , laypage = layui.laypage
             , element = layui.element
             , util = layui.util
-            , $ = layui.$
             , tag = layui.tag;
 
         //欢迎信息
         layer.msg('Hello World');
         // 渲染 nav 导航菜单
         element.render('nav');
+        // tab 切换事件
+        element.on('tab(body-handle)', function(data){
+            console.log(data.index); // 得到当前 tab 项的所在下标
+            if (data.index === 0) {
+                $("#jsi-flying-fish-container").empty();
+                RENDERER.init();
+            }
+        });
 
         tag.on('click(demo)', function (data) {
             console.log('点击');

+ 126 - 0
templates/iframe-comic-details.html

@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+    <title>IOmga</title>
+    <link rel="stylesheet" href="{{ url_for('static', filename='bootstrap-v4.6.2/css/bootstrap.min.css') }}">
+    <link rel="stylesheet" href="{{ url_for('static', filename='layui-v2.9.7/css/layui.css') }}">
+    <style>
+        /* 字体集 */
+        @font-face {
+            font-family: "思源黑体 Regular";
+            font-weight: 400;
+            src: url("{{ url_for('static', filename='font/SourceHanSans.Regular.woff2') }}") format("woff2"),
+            url("{{ url_for('static', filename='font/SourceHanSans.Regular.woff') }}") format("woff");
+            font-display: swap;
+        }
+
+        body, html {
+            height: 100%;
+            margin: 0;
+            padding: 0;
+            font-family: '思源黑体 Regular', Helvetica, 'Microsoft YaHei', Arial, sans-serif;
+        }
+
+        .comic-header {
+            margin-top: 25px;
+        }
+
+        .comic-cover {
+            flex-shrink: 0;
+            width: 196px;
+            height: 261px;
+        }
+
+        .comic-work {
+            padding: 0 15px;
+            height: 261px;
+        }
+
+        .work-desc {
+            padding-top: 16px;
+        }
+
+        .comic-body {
+            height: calc(100% - 261px - 25px - 25px);
+            margin-top: 25px;
+        }
+    </style>
+</head>
+<body>
+<div class="layui-layout layui-fluid" style="height: 100%; padding: 0">
+    <div class="layui-row" style="height: 100%">
+        <div class="layui-col-xs8 layui-col-xs-offset2 layui-col-md6 layui-col-md-offset3" style="height: 100%;">
+            <div class="layui-row comic-header">
+                <div class="layui-col-xs4">
+                    <img src="https://www.wetools.com/placeholder/160x213?fontsize=30" class="comic-cover rounded" alt="...">
+                </div>
+                <div class="layui-col-xs8 comic-work">
+                    <div class="layui-text">
+                        <h2>你当像鸟飞往你的山</h2>
+                        <p class="overflow-auto" style="height: 154px">
+                            人们只看到我的与众不同:一个十七岁前从未踏入教室的大山女孩,却戴上一顶学历的高帽,熠熠生辉。只有我知道自己的真面目:我来自一个极少有人能想象的家庭。我的童年由垃圾场的废铜烂铁铸成,那里没有读书声,只有起重机的轰鸣。不上学,不就医,是父亲要我们坚持的忠诚与真理。父亲不允许我们拥有自己的声音,我们的意志是他眼中的恶魔。哈佛大学,剑桥大学,哲学硕士,历史博士……我知道,像我这样从垃圾堆里爬出来的无知女孩,能取得如今的成就,应当感激涕零才对。但我丝毫提不起热情。我曾怯懦、崩溃、自我怀疑,内心里有什么东西腐烂了,恶臭熏天。直到我逃离大山,打开另一个世界。那是教育给我的新世界,那是我生命的无限可能。
+                        </p>
+                    </div>
+                    <div class="layui-row work-desc">
+                        <div class="layui-row">
+                            <div class="layui-col-xs6">
+                                <p>作者: 塔拉·韦斯特弗</p>
+                            </div>
+                            <div class="layui-col-xs6">
+                                <p>标签: 文学、励志</p>
+                            </div>
+                        </div>
+                        <div class="layui-row" style="padding-top: 8px">
+                            <p>更新日期: 2019-10-24</p>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="layui-row comic-body border border-secondary rounded">
+                <div class="layui-row"></div>
+                <div class="layui-row"></div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 引入 layui.js 的 <script> 标签最好放置在 html 末尾 -->
+<script src="{{ url_for('static', filename='js/jquery-3.7.1.min.js') }}"></script>
+<script src="{{ url_for('static', filename='bootstrap-v4.6.2/js/bootstrap.bundle.min.js') }}"></script>
+<script src="{{ url_for('static', filename='layui-v2.9.7/layui.js') }}"></script>
+<script>
+    $(function () {
+        $('[data-toggle="tooltip"]').tooltip()
+    })
+
+    layui.config({
+        base: "{{ url_for('static', filename='layui-modules/') }}",
+        version: true
+    }).use(["layer", "form", "laypage", "element", "util"], function () {
+        var layer = layui.layer
+            , form = layui.form
+            , laypage = layui.laypage
+            , element = layui.element
+            , util = layui.util;
+
+        // tab 删除事件
+        element.on('tabDelete(body-handle)', function (data) {
+            console.log(data.index); // 得到被删除的 tab 项的所在下标
+            console.log(data.elem); // 得到当前的 tab 容器
+        });
+
+        // 自定义每页条数的选择项
+        laypage.render({
+            elem: 'card-laypage-limits',
+            count: 1000,
+            limit: 100,
+            limits: [100, 300, 500],
+            layout: ['prev', 'page', 'next', 'limit']
+        });
+
+    });
+</script>
+</body>
+</html>

+ 49 - 38
templates/search-container.html

@@ -3,54 +3,65 @@
     <div class="layui-tab" lay-filter="body-handle" lay-allowclose="true" style="height: 100%; margin: 0">
         <ul class="layui-tab-title" style="padding-top: 10px;">
             <li id="search-tab" lay-id="search">搜索</li>
-            <li id="search-tab" class="layui-this" lay-id="search-result">搜索结果</li>
+            <li lay-id="search-result">搜索结果</li>
+            <li class="layui-this" lay-id="comic-details">漫画详情</li>
         </ul>
         <div class="layui-tab-content index-tab-content">
-            <div class="layui-tab-item">
-                <div class="search-body-container gradient">
-                    <div class="search-main-container">
-                        <div class="layui-text" style=" ">
-                            <h1>欢迎使用XXXXXX</h1>
-                        </div>
-                        <form class="layui-form" action="" lay-filter="search-form">
-                            <div class="layui-form-item">
-                                <div class="layui-input-group" style="width: 100%">
-                                    <input name="search" type="text" placeholder="请输入想要搜寻的XXX..."
-                                           class="layui-input">
-                                    <div class="layui-input-split layui-input-suffix layui-bg-green"
-                                         style="cursor: pointer; width: 1px" lay-on="openSearch">
-                                        <i class="layui-icon layui-icon-search my-icon"></i>
-                                        搜索
+            <div class="layui-tab-item layui-show">
+                <div class="fish-gradient" style="height: 100%; width: 100%; position: relative">
+                    <div class="search-body-container">
+                        <div class="search-main-container">
+                            <div class="layui-text" style=" ">
+                                <h1>欢迎使用XXXXXX</h1>
+                            </div>
+                            <form class="layui-form" action="" lay-filter="search-form">
+                                <div class="layui-form-item">
+                                    <div class="layui-input-group" style="width: 100%">
+                                        <input name="search" type="text" placeholder="请输入想要搜寻的XXX..."
+                                               class="layui-input">
+                                        <div class="layui-input-split layui-input-suffix layui-bg-green"
+                                             style="cursor: pointer; width: 1px" lay-on="openSearch">
+                                            <i class="layui-icon layui-icon-search my-icon"></i>
+                                            搜索
+                                        </div>
                                     </div>
                                 </div>
-                            </div>
-                            <div class="layui-form-item">
-                                <label class="layui-form-label"
-                                       style="padding-left: 0; text-align: left; width: 100px">选择搜索平台:</label>
-                                <div class="layui-input-block" style="width: 180px; margin-left: 115px">
-                                    <select name="platform" lay-filter="platform">
-                                        <option value="0" selected>聚合搜索</option>
-                                        <option value="1">AAAA</option>
-                                        <option value="2">BBBB</option>
-                                        <option value="3">CCCC</option>
-                                        <option value="4">DDDD</option>
-                                    </select>
+                                <div class="layui-form-item">
+                                    <label class="layui-form-label"
+                                           style="padding-left: 0; text-align: left; width: 100px">选择搜索平台:</label>
+                                    <div class="layui-input-block" style="width: 180px; margin-left: 115px">
+                                        <select name="platform" lay-filter="platform">
+                                            <option value="0" selected>聚合搜索</option>
+                                            <option value="1">AAAA</option>
+                                            <option value="2">BBBB</option>
+                                            <option value="3">CCCC</option>
+                                            <option value="4">DDDD</option>
+                                        </select>
+                                    </div>
                                 </div>
+                            </form>
+                            <div class="layui-btn-container tag" lay-filter="search_history" lay-allowclose="true"
+                                 lay-newTag="">
+                                <button lay-id="11" type="button" class="tag-item">网站设置</button>
+                                <button lay-id="22" type="button" class="tag-item">用户管理</button>
+                                <button lay-id="33" type="button" class="tag-item">权限分配</button>
+                                <button lay-id="44" type="button" class="tag-item">商品管理</button>
+                                <button lay-id="55" type="button" class="tag-item">订单管理</button>
                             </div>
-                        </form>
-                        <div class="layui-btn-container tag" lay-filter="search_history" lay-allowclose="true"
-                             lay-newTag="">
-                            <button lay-id="11" type="button" class="tag-item">网站设置</button>
-                            <button lay-id="22" type="button" class="tag-item">用户管理</button>
-                            <button lay-id="33" type="button" class="tag-item">权限分配</button>
-                            <button lay-id="44" type="button" class="tag-item">商品管理</button>
-                            <button lay-id="55" type="button" class="tag-item">订单管理</button>
                         </div>
                     </div>
+
+                    <!--承载游动小鱼特效的div容器-->
+                    <div id="jsi-flying-fish-container" class="fish-container"></div>
                 </div>
             </div>
-            <div class="layui-tab-item layui-show">
-                <iframe src="/SearchPage?search=XXX&platform=0" id="280" frameborder="0" scrolling="auto" style="height: 100%; width: 100%; display: block;"></iframe>
+            <div class="layui-tab-item">
+                <iframe src="/SearchPage?search=XXX&platform=0" id="280" frameborder="0" scrolling="auto"
+                        style="height: 100%; width: 100%; display: block;"></iframe>
+            </div>
+            <div class="layui-tab-item">
+                <iframe src="/ComicDetails/新华文轩/你当像鸟飞往你的山" id="280" frameborder="0" scrolling="auto"
+                        style="height: 100%; width: 100%; display: block;"></iframe>
             </div>
         </div>
     </div>