Compare commits

..

No commits in common. "c7830c3f177a825b202b9fac7ba142509e3663b4" and "3fddd1cf5c9c98114ab5c070edba0ddc39a4ea78" have entirely different histories.

8 changed files with 237 additions and 150 deletions

View File

@ -30,9 +30,10 @@ Melpomene is mainly one JS file and one CSS file. You only need to include them
The JS files expect you to write some very specific HTML tags to be able to work. The JS files expect you to write some very specific HTML tags to be able to work.
To simplify things, you only need to copy-paste the content of `melpomene.html` into your own page and change a few things : To simplify things, you only need to copy-paste the content of `melpomene.html` into your own page and change a few things :
+ `data-pages-width` must be set to your comic pages width in px
+ `data-pages-height` must be set to your comic pages height in px
+ You must duplicate the `img` tag for each of you comic page and : + You must duplicate the `img` tag for each of you comic page and :
+ set `url` to the actual URL of your page + set `url` to the actual URL of your page
+ set `height` and `width` to the actual image sizes
+ set `data-zooms` with the zooms information, like so : `<zoom 1 width>, <zoom 1 height>, <zoom 1 x offset>, <zoom 1 y offset>; <zoom 2 width> ...` + set `data-zooms` with the zooms information, like so : `<zoom 1 width>, <zoom 1 height>, <zoom 1 x offset>, <zoom 1 y offset>; <zoom 2 width> ...`
+ example : `<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P01.jpg" data-zooms="2481.0,1327.1057,0.0,0.0;593.15338,1076.4635,0.0,1364.053;890.72864,491.29874,830.81415,1751.5;2481.0,1078.4192,0.0,1364.053;562.77032,909.44702,102.48115,2491.6567;920.74463,909.44702,698.55927,2491.6567;728.776,909.44702,1652.3695,2491.6567"/>` + example : `<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P01.jpg" data-zooms="2481.0,1327.1057,0.0,0.0;593.15338,1076.4635,0.0,1364.053;890.72864,491.29874,830.81415,1751.5;2481.0,1078.4192,0.0,1364.053;562.77032,909.44702,102.48115,2491.6567;920.74463,909.44702,698.55927,2491.6567;728.776,909.44702,1652.3695,2491.6567"/>`
@ -44,6 +45,7 @@ The following limitations are known and will be improved upon :
+ Mobile support is currently limited + Mobile support is currently limited
+ There are some performences issues + There are some performences issues
+ Your comic pages need to be the same size
# How to setup Melpomene ? # How to setup Melpomene ?
@ -80,14 +82,6 @@ If you wish to run a custom generation process, this generator can output a JSON
You are now ready to integrate Melpomene in your website! You are now ready to integrate Melpomene in your website!
## Advanced usage
If you need to do some global scaling / offset of all zooms in HTML (if for example you reuse zooms data for multiple resolutions), you can add the following attributes to the `<div id="reader-pages" ...>` tag :
+ `data-global-zoom-offset="<x offset float value>,<y offset float value>"` : offset all positions by the provided x / y values
+ If they become negative, they get clamped to 0 and width / height get reduced to compensate
+ If they become greater than the page size, they get clamped to the page size and width / height get reduced to compensate
+ `data-global-zoom-scale="<float value>"` : scale all positions / sizes by this factor
# Credits # Credits

83
demos/demo_data.js Normal file
View File

@ -0,0 +1,83 @@
PAGES_ZOOMS = [
[1, 2481.0, 1327.1057, 0.0, 0.0],
[1, 593.15338, 1076.4635, 0.0, 1364.053],
[1, 890.72864, 491.29874, 830.81415, 1751.5],
[1, 2481.0, 1078.4192, 0.0, 1364.053],
[1, 562.77032, 909.44702, 102.48115, 2491.6567],
[1, 920.74463, 909.44702, 698.55927, 2491.6567],
[1, 728.776, 909.44702, 1652.3695, 2491.6567],
[2, 1459.9161, 960.62878, 99.857468, 103.85177],
[2, 788.87384, 960.62878, 1593.7252, 103.85177],
[2, 455.35007, 305.56384, 389.44412, 1376.0359],
[2, 2282.7415, 760.914, 99.857475, 1114.4093],
[2, 1069.9728, 496.29166, 101.85461, 1928.2478],
[2, 1209.7733, 496.29166, 1172.3267, 1928.2478],
[2, 788.87402, 926.67731, 101.85462, 2474.468],
[2, 415.40707, 926.67731, 924.68018, 2474.468],
[2, 1008.5604, 926.67731, 1372.0416, 2474.468],
[3, 2278.4634, 1424.0264, 103.9937, 103.9937],
[3, 457.3472, 589.15906, 816.83411, 469.33011],
[3, 618.69055, 832.04285, 104.47829, 1578.7172],
[3, 1170.7311, 832.04285, 756.71887, 1578.7172],
[3, 418.14838, 832.04285, 1962.6545, 1578.7172],
[3, 2280.6614, 940.57422, 100.82664, 2461.1147],
[4, 1128.3893, 547.21899, 100.85604, 101.85461],
[4, 1125.3937, 505.27878, 101.85462, 700.99945],
[4, 659.59674, 1104.1381, 1237.9126, 100.46677],
[4, 474.09705, 1104.7645, 1908.0801, 103.04887],
[4, 991.58466, 641.08496, 365.47833, 1256.2069],
[4, 2276.7502, 1268.1899, 101.85461, 1256.2069],
[4, 669.66565, 827.24689, 100.95136, 2574.8799],
[4, 1574.1992, 825.24969, 806.56573, 2576.877],
[5, 2270.7588, 1348.0758, 107.84606, 109.84322],
[5, 828.80096, 1879.4762, 104.70337, 1519.7256],
[5, 606.50098, 1877.479, 975.78717, 1521.7228],
[5, 755.02411, 1139.5406, 1623.401, 1521.7228],
[5, 757.02124, 680.4198, 1621.4038, 2716.7849],
[6, 2273.6387, 1248.3829, 104.50265, 107.32705],
[6, 624.19147, 440.60574, 1259.6805, 771.06006],
[6, 830.37231, 940.5238, 110.15143, 1415.0222],
[6, 1395.2515, 1313.3441, 985.71411, 1409.3734],
[6, 1321.8173, 796.47961, 112.97583, 2417.6829],
[6, 974.4165, 754.11365, 1403.7247, 2646.4587],
[7, 828.81702, 497.29019, 443.36716, 531.2417],
[7, 2275.0127, 1188.0789, 105.30553, 109.78442],
[7, 2275.0127, 804.28339, 105.30553, 1356.3481],
[7, 847.87823, 1182.9146, 105.30553, 2210.6436],
[7, 497.6918, 1185.7389, 994.37964, 2213.468],
[7, 854.35431, 1185.7389, 1528.7885, 2213.468],
[8, 2272.7561, 1228.2468, 101.85461, 107.84607],
[8, 497.29019, 756.91962, 994.58044, 569.18756],
[8, 2276.7502, 1052.4977, 101.85461, 1336.0929],
[8, 2278.7473, 958.63165, 101.85462, 2444.5107],
[9, 1131.1705, 453.31552, 101.67825, 103.09045],
[9, 1148.1168, 453.31552, 1232.8488, 103.09045],
[9, 1481.6407, 453.31552, 501.89209, 103.09045],
[9, 2282.1118, 855.79193, 98.853851, 604.42072],
[9, 1776.5449, 881.21149, 290.91275, 1355.71],
[9, 703.27454, 398.23981, 762.58685, 2863.9373],
[9, 2270.8142, 1132.5826, 107.32704, 2270.8142],
[10, 2278.7473, 1240.2297, 101.85461, 101.85461],
[10, 667.04791, 1016.549, 99.857468, 1390.016],
[10, 1583.7395, 1014.5519, 798.85974, 1390.016],
[10, 2280.7446, 944.65161, 99.857468, 2456.4937],
[11, 830.81415, 1194.2953, 97.860321, 101.85461],
[11, 774.89398, 1198.2897, 960.62885, 99.857468],
[11, 609.13055, 1192.2982, 1769.4744, 101.85461],
[11, 363.4812, 868.75995, 99.857468, 1348.0758],
[11, 1495.8649, 870.75714, 493.2959, 1346.0787],
[11, 357.48975, 870.75714, 2023.1123, 1348.0758],
[11, 529.24457, 499.28735, 103.85177, 2280.7446],
[11, 531.24176, 687.01941, 99.857468, 2716.123],
[11, 1022.5405, 1120.4008, 669.04504, 2280.7446],
[11, 657.06213, 1120.4008, 1725.5371, 2280.7446],
[12, 704.99371, 341.51254, 619.11633, 281.59805],
[12, 393.43842, 551.21326, 1459.9161, 159.77196],
[12, 2280.7446, 691.01367, 99.857468, 101.85461],
[12, 718.97375, 1198.2897, 99.857468, 840.79987],
[12, 712.9823, 1196.2925, 850.78564, 842.79706],
[12, 780.88538, 1192.2982, 1599.7167, 842.79706],
[12, 2366.6221, 1445.9362, 61.911629, 2049.0752],
[12, 922.68298, 551.21326, 984.59467, 2378.605],
[12, 631.09918, 211.69783, 1851.3574, 3289.3049],
]

View File

@ -15,19 +15,19 @@
<!-- CC-BY-NC-SA https://git.aribaud.net/caribaud/melpomene/ --> <!-- CC-BY-NC-SA https://git.aribaud.net/caribaud/melpomene/ -->
<div id="reader-frame"> <div id="reader-frame">
<div id="reader-content-frame"> <div id="reader-content-frame">
<div id="reader-pages" class="animated" hidden> <div id="reader-pages" class="animated" data-pages-width="2481" data-pages-height="3503" hidden>
<img loading="lazy" width="2481" height="3503" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P01.jpg" data-zooms="2481.0,1327.1057,0.0,0.0;593.15338,1076.4635,0.0,1364.053;890.72864,491.29874,830.81415,1751.5;2481.0,1078.4192,0.0,1364.053;562.77032,909.44702,102.48115,2491.6567;920.74463,909.44702,698.55927,2491.6567;728.776,909.44702,1652.3695,2491.6567"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P01.jpg"/>
<img loading="lazy" width="2481" height="3503" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P02.jpg" data-zooms="1459.9161,960.62878,99.857468,103.85177;788.87384,960.62878,1593.7252,103.85177;455.35007,305.56384,389.44412,1376.0359;2282.7415,760.914,99.857475,1114.4093;1069.9728,496.29166,101.85461,1928.2478;1209.7733,496.29166,1172.3267,1928.2478;788.87402,926.67731,101.85462,2474.468;415.40707,926.67731,924.68018,2474.468;1008.5604,926.67731,1372.0416,2474.468"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P02.jpg"/>
<img loading="lazy" width="2481" height="3503" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P03.jpg" data-zooms="2278.4634,1424.0264,103.9937,103.9937;457.3472,589.15906,816.83411,469.33011;618.69055,832.04285,104.47829,1578.7172;1170.7311,832.04285,756.71887,1578.7172;418.14838,832.04285,1962.6545,1578.7172;2280.6614,940.57422,100.82664,2461.1147"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P03.jpg"/>
<img loading="lazy" width="2481" height="3503" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P04.jpg" data-zooms="1128.3893,547.21899,100.85604,101.85461;1125.3937,505.27878,101.85462,700.99945;659.59674,1104.1381,1237.9126,100.46677;474.09705,1104.7645,1908.0801,103.04887;991.58466,641.08496,365.47833,1256.2069;2276.7502,1268.1899,101.85461,1256.2069;669.66565,827.24689,100.95136,2574.8799;1574.1992,825.24969,806.56573,2576.877"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P04.jpg"/>
<img loading="lazy" width="2481" height="3503" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P05.jpg" data-zooms="2270.7588,1348.0758,107.84606,109.84322;828.80096,1879.4762,104.70337,1519.7256;606.50098,1877.479,975.78717,1521.7228;755.02411,1139.5406,1623.401,1521.7228;757.02124,680.4198,1621.4038,2716.7849"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P05.jpg"/>
<img loading="lazy" width="2481" height="3503" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P06.jpg" data-zooms="2273.6387,1248.3829,104.50265,107.32705;624.19147,440.60574,1259.6805,771.06006;830.37231,940.5238,110.15143,1415.0222;1395.2515,1313.3441,985.71411,1409.3734;1321.8173,796.47961,112.97583,2417.6829;974.4165,754.11365,1403.7247,2646.4587"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P06.jpg"/>
<img loading="lazy" width="2481" height="3503" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P07.jpg" data-zooms="828.81702,497.29019,443.36716,531.2417;2275.0127,1188.0789,105.30553,109.78442;2275.0127,804.28339,105.30553,1356.3481;847.87823,1182.9146,105.30553,2210.6436;497.6918,1185.7389,994.37964,2213.468;854.35431,1185.7389,1528.7885,2213.468"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P07.jpg"/>
<img loading="lazy" width="2481" height="3503" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P08.jpg" data-zooms="2272.7561,1228.2468,101.85461,107.84607;497.29019,756.91962,994.58044,569.18756;2276.7502,1052.4977,101.85461,1336.0929;2278.7473,958.63165,101.85462,2444.5107"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P08.jpg"/>
<img loading="lazy" width="2481" height="3503" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P09.jpg" data-zooms="1131.1705,453.31552,101.67825,103.09045;1148.1168,453.31552,1232.8488,103.09045;1481.6407,453.31552,501.89209,103.09045;2282.1118,855.79193,98.853851,604.42072;1776.5449,881.21149,290.91275,1355.71;703.27454,398.23981,762.58685,2863.9373;2270.8142,1132.5826,107.32704,2270.8142"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P09.jpg"/>
<img loading="lazy" width="2481" height="3503" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P10.jpg" data-zooms="2278.7473,1240.2297,101.85461,101.85461;667.04791,1016.549,99.857468,1390.016;1583.7395,1014.5519,798.85974,1390.016;2280.7446,944.65161,99.857468,2456.4937"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P10.jpg"/>
<img loading="lazy" width="2481" height="3503" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P11.jpg" data-zooms="830.81415,1194.2953,97.860321,101.85461;774.89398,1198.2897,960.62885,99.857468;609.13055,1192.2982,1769.4744,101.85461;363.4812,868.75995,99.857468,1348.0758;1495.8649,870.75714,493.2959,1346.0787;357.48975,870.75714,2023.1123,1348.0758;529.24457,499.28735,103.85177,2280.7446;531.24176,687.01941,99.857468,2716.123;1022.5405,1120.4008,669.04504,2280.7446;657.06213,1120.4008,1725.5371,2280.7446"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P11.jpg"/>
<img loading="lazy" width="2481" height="3503" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P12.jpg" data-zooms="704.99371,341.51254,619.11633,281.59805;393.43842,551.21326,1459.9161,159.77196;2280.7446,691.01367,99.857468,101.85461;718.97375,1198.2897,99.857468,840.79987;712.9823,1196.2925,850.78564,842.79706;780.88538,1192.2982,1599.7167,842.79706;2366.6221,1445.9362,61.911629,2049.0752;922.68298,551.21326,984.59467,2378.605;631.09918,211.69783,1851.3574,3289.3049"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P12.jpg"/>
</div> </div>
<div id="focus-overlay" class="flex-col fill"> <div id="focus-overlay" class="flex-col fill">
<div class="grow obscured animated"></div> <div class="grow obscured animated"></div>
@ -60,10 +60,10 @@
</div> </div>
<!-- End of Melpomene comic reader --> <!-- End of Melpomene comic reader -->
</body> </body>
<!-- melpomene_js.html import --> <!-- melpomene_js.html import -->
<script src="demo_data.js"></script>
<script src="../melpomene.js"></script> <script src="../melpomene.js"></script>
</html> </html>

View File

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="demo.css">
<!-- melpomene_head.html import -->
<link rel="stylesheet" href="../melpomene.css">
</head>
<body>
<!-- Melpomene comic reader -->
<!-- CC-BY-NC-SA https://git.aribaud.net/caribaud/melpomene/ -->
<div id="reader-frame">
<div id="reader-content-frame">
<div id="reader-pages" class="animated" data-pages-width="2481" data-pages-height="3503" hidden>
<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P01.jpg" data-zooms="2481.0,1327.1057,0.0,0.0;593.15338,1076.4635,0.0,1364.053;890.72864,491.29874,830.81415,1751.5;2481.0,1078.4192,0.0,1364.053;562.77032,909.44702,102.48115,2491.6567;920.74463,909.44702,698.55927,2491.6567;728.776,909.44702,1652.3695,2491.6567"/>
<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P02.jpg" data-zooms="1459.9161,960.62878,99.857468,103.85177;788.87384,960.62878,1593.7252,103.85177;455.35007,305.56384,389.44412,1376.0359;2282.7415,760.914,99.857475,1114.4093;1069.9728,496.29166,101.85461,1928.2478;1209.7733,496.29166,1172.3267,1928.2478;788.87402,926.67731,101.85462,2474.468;415.40707,926.67731,924.68018,2474.468;1008.5604,926.67731,1372.0416,2474.468"/>
<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P03.jpg" data-zooms="2278.4634,1424.0264,103.9937,103.9937;457.3472,589.15906,816.83411,469.33011;618.69055,832.04285,104.47829,1578.7172;1170.7311,832.04285,756.71887,1578.7172;418.14838,832.04285,1962.6545,1578.7172;2280.6614,940.57422,100.82664,2461.1147"/>
<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P04.jpg" data-zooms="1128.3893,547.21899,100.85604,101.85461;1125.3937,505.27878,101.85462,700.99945;659.59674,1104.1381,1237.9126,100.46677;474.09705,1104.7645,1908.0801,103.04887;991.58466,641.08496,365.47833,1256.2069;2276.7502,1268.1899,101.85461,1256.2069;669.66565,827.24689,100.95136,2574.8799;1574.1992,825.24969,806.56573,2576.877"/>
<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P05.jpg" data-zooms="2270.7588,1348.0758,107.84606,109.84322;828.80096,1879.4762,104.70337,1519.7256;606.50098,1877.479,975.78717,1521.7228;755.02411,1139.5406,1623.401,1521.7228;757.02124,680.4198,1621.4038,2716.7849"/>
<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P06.jpg" data-zooms="2273.6387,1248.3829,104.50265,107.32705;624.19147,440.60574,1259.6805,771.06006;830.37231,940.5238,110.15143,1415.0222;1395.2515,1313.3441,985.71411,1409.3734;1321.8173,796.47961,112.97583,2417.6829;974.4165,754.11365,1403.7247,2646.4587"/>
<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P07.jpg" data-zooms="828.81702,497.29019,443.36716,531.2417;2275.0127,1188.0789,105.30553,109.78442;2275.0127,804.28339,105.30553,1356.3481;847.87823,1182.9146,105.30553,2210.6436;497.6918,1185.7389,994.37964,2213.468;854.35431,1185.7389,1528.7885,2213.468"/>
<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P08.jpg" data-zooms="2272.7561,1228.2468,101.85461,107.84607;497.29019,756.91962,994.58044,569.18756;2276.7502,1052.4977,101.85461,1336.0929;2278.7473,958.63165,101.85462,2444.5107"/>
<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P09.jpg" data-zooms="1131.1705,453.31552,101.67825,103.09045;1148.1168,453.31552,1232.8488,103.09045;1481.6407,453.31552,501.89209,103.09045;2282.1118,855.79193,98.853851,604.42072;1776.5449,881.21149,290.91275,1355.71;703.27454,398.23981,762.58685,2863.9373;2270.8142,1132.5826,107.32704,2270.8142"/>
<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P10.jpg" data-zooms="2278.7473,1240.2297,101.85461,101.85461;667.04791,1016.549,99.857468,1390.016;1583.7395,1014.5519,798.85974,1390.016;2280.7446,944.65161,99.857468,2456.4937"/>
<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P11.jpg" data-zooms="830.81415,1194.2953,97.860321,101.85461;774.89398,1198.2897,960.62885,99.857468;609.13055,1192.2982,1769.4744,101.85461;363.4812,868.75995,99.857468,1348.0758;1495.8649,870.75714,493.2959,1346.0787;357.48975,870.75714,2023.1123,1348.0758;529.24457,499.28735,103.85177,2280.7446;531.24176,687.01941,99.857468,2716.123;1022.5405,1120.4008,669.04504,2280.7446;657.06213,1120.4008,1725.5371,2280.7446"/>
<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P12.jpg" data-zooms="704.99371,341.51254,619.11633,281.59805;393.43842,551.21326,1459.9161,159.77196;2280.7446,691.01367,99.857468,101.85461;718.97375,1198.2897,99.857468,840.79987;712.9823,1196.2925,850.78564,842.79706;780.88538,1192.2982,1599.7167,842.79706;2366.6221,1445.9362,61.911629,2049.0752;922.68298,551.21326,984.59467,2378.605;631.09918,211.69783,1851.3574,3289.3049"/>
</div>
<div id="focus-overlay" class="flex-col fill">
<div class="grow obscured animated"></div>
<div id="focus-overlay-height" class="flex animated" style="height:100%">
<div class="grow obscured animated"></div>
<div id="focus-overlay-width" class="focus animated" style="width:100%"></div>
<div class="grow obscured animated"></div>
</div>
<div class="grow obscured animated"></div>
</div>
<div id="nav-controls" class="fill">
<div class="left" id="nav-left" onclick="moveReader(false,false)"></div>
<div class="right" id="nav-right" onclick="moveReader(true,false)"></div>
</div>
<div id="help-menu">
<div id="help-controls" style="opacity:1; transform: translate(0,0);">
<div><div class="key">&larr;</div>/ scroll up / clic : previous</div>
<div><div class="key">&rarr;</div>/ scroll down / clic : next</div>
<div>-----------------------</div>
<div><div class="key">F</div>: Toggle fullscreen</div>
<div><div class="key">P</div>: Toggle progress bar</div>
<div><div class="key">V</div>: Toggle panel / page viewing mode</div>
</div>
</div>
</div>
<div id="reader-progress-container">
<div id="reader-progress-bar"></div>
<div id="reader-progress-pages"></div>
</div>
</div>
<!-- End of Melpomene comic reader -->
</body>
<!-- melpomene_js.html import -->
<script src="../melpomene.js"></script>
</html>

View File

@ -11,24 +11,28 @@
<body> <body>
<!-- melpomene_reader.html import -->
<!-- Melpomene comic reader --> <!-- Melpomene comic reader -->
<!-- CC-BY-NC-SA https://git.aribaud.net/caribaud/melpomene/ --> <!-- CC-BY-NC-SA https://git.aribaud.net/caribaud/melpomene/ -->
<div id="reader-frame"> <div id="reader-frame">
<div id="reader-content-frame"> <div id="reader-content-frame">
<div id="reader-pages" class="animated" data-global-zoom-scale="0.4836759371221282" data-global-zoom-offset="0,-70" hidden>
<img loading="lazy" width="1200" height="1660" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P01.jpg" data-zooms="2481.0,1327.1057,0.0,0.0;593.15338,1076.4635,0.0,1364.053;890.72864,491.29874,830.81415,1751.5;2481.0,1078.4192,0.0,1364.053;562.77032,909.44702,102.48115,2491.6567;920.74463,909.44702,698.55927,2491.6567;728.776,909.44702,1652.3695,2491.6567"/> <div id="reader-pages" class="animated" data-pages-width="2481" data-pages-height="3503" hidden>
<img loading="lazy" width="1200" height="1660" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P02.jpg" data-zooms="1459.9161,960.62878,99.857468,103.85177;788.87384,960.62878,1593.7252,103.85177;455.35007,305.56384,389.44412,1376.0359;2282.7415,760.914,99.857475,1114.4093;1069.9728,496.29166,101.85461,1928.2478;1209.7733,496.29166,1172.3267,1928.2478;788.87402,926.67731,101.85462,2474.468;415.40707,926.67731,924.68018,2474.468;1008.5604,926.67731,1372.0416,2474.468"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P01.jpg"/>
<img loading="lazy" width="1200" height="1660" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P03.jpg" data-zooms="2278.4634,1424.0264,103.9937,103.9937;457.3472,589.15906,816.83411,469.33011;618.69055,832.04285,104.47829,1578.7172;1170.7311,832.04285,756.71887,1578.7172;418.14838,832.04285,1962.6545,1578.7172;2280.6614,940.57422,100.82664,2461.1147"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P02.jpg"/>
<img loading="lazy" width="1200" height="1660" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P04.jpg" data-zooms="1128.3893,547.21899,100.85604,101.85461;1125.3937,505.27878,101.85462,700.99945;659.59674,1104.1381,1237.9126,100.46677;474.09705,1104.7645,1908.0801,103.04887;991.58466,641.08496,365.47833,1256.2069;2276.7502,1268.1899,101.85461,1256.2069;669.66565,827.24689,100.95136,2574.8799;1574.1992,825.24969,806.56573,2576.877"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P03.jpg"/>
<img loading="lazy" width="1200" height="1660" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P05.jpg" data-zooms="2270.7588,1348.0758,107.84606,109.84322;828.80096,1879.4762,104.70337,1519.7256;606.50098,1877.479,975.78717,1521.7228;755.02411,1139.5406,1623.401,1521.7228;757.02124,680.4198,1621.4038,2716.7849"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P04.jpg"/>
<img loading="lazy" width="1200" height="1660" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P06.jpg" data-zooms="2273.6387,1248.3829,104.50265,107.32705;624.19147,440.60574,1259.6805,771.06006;830.37231,940.5238,110.15143,1415.0222;1395.2515,1313.3441,985.71411,1409.3734;1321.8173,796.47961,112.97583,2417.6829;974.4165,754.11365,1403.7247,2646.4587"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P05.jpg"/>
<img loading="lazy" width="1200" height="1660" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P07.jpg" data-zooms="828.81702,497.29019,443.36716,531.2417;2275.0127,1188.0789,105.30553,109.78442;2275.0127,804.28339,105.30553,1356.3481;847.87823,1182.9146,105.30553,2210.6436;497.6918,1185.7389,994.37964,2213.468;854.35431,1185.7389,1528.7885,2213.468"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P06.jpg"/>
<img loading="lazy" width="1200" height="1660" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P08.jpg" data-zooms="2272.7561,1228.2468,101.85461,107.84607;497.29019,756.91962,994.58044,569.18756;2276.7502,1052.4977,101.85461,1336.0929;2278.7473,958.63165,101.85462,2444.5107"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P07.jpg"/>
<img loading="lazy" width="1200" height="1660" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P09.jpg" data-zooms="1131.1705,453.31552,101.67825,103.09045;1148.1168,453.31552,1232.8488,103.09045;1481.6407,453.31552,501.89209,103.09045;2282.1118,855.79193,98.853851,604.42072;1776.5449,881.21149,290.91275,1355.71;703.27454,398.23981,762.58685,2863.9373;2270.8142,1132.5826,107.32704,2270.8142"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P08.jpg"/>
<img loading="lazy" width="1200" height="1660" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P10.jpg" data-zooms="2278.7473,1240.2297,101.85461,101.85461;667.04791,1016.549,99.857468,1390.016;1583.7395,1014.5519,798.85974,1390.016;2280.7446,944.65161,99.857468,2456.4937"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P09.jpg"/>
<img loading="lazy" width="1200" height="1660" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P11.jpg" data-zooms="830.81415,1194.2953,97.860321,101.85461;774.89398,1198.2897,960.62885,99.857468;609.13055,1192.2982,1769.4744,101.85461;363.4812,868.75995,99.857468,1348.0758;1495.8649,870.75714,493.2959,1346.0787;357.48975,870.75714,2023.1123,1348.0758;529.24457,499.28735,103.85177,2280.7446;531.24176,687.01941,99.857468,2716.123;1022.5405,1120.4008,669.04504,2280.7446;657.06213,1120.4008,1725.5371,2280.7446"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P10.jpg"/>
<img loading="lazy" width="1200" height="1660" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P12.jpg" data-zooms="704.99371,341.51254,619.11633,281.59805;393.43842,551.21326,1459.9161,159.77196;2280.7446,691.01367,99.857468,101.85461;718.97375,1198.2897,99.857468,840.79987;712.9823,1196.2925,850.78564,842.79706;780.88538,1192.2982,1599.7167,842.79706;2366.6221,1445.9362,61.911629,2049.0752;922.68298,551.21326,984.59467,2378.605;631.09918,211.69783,1851.3574,3289.3049"/> <img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P11.jpg"/>
<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/low-res/en_Pepper-and-Carrot_by-David-Revoy_E35P12.jpg"/>
</div> </div>
<div id="focus-overlay" class="flex-col fill"> <div id="focus-overlay" class="flex-col fill">
<div class="grow obscured animated"></div> <div class="grow obscured animated"></div>
<div id="focus-overlay-height" class="flex animated" style="height:100%"> <div id="focus-overlay-height" class="flex animated" style="height:100%">
@ -38,10 +42,12 @@
</div> </div>
<div class="grow obscured animated"></div> <div class="grow obscured animated"></div>
</div> </div>
<div id="nav-controls" class="fill"> <div id="nav-controls" class="fill">
<div class="left" id="nav-left" onclick="moveReader(false,false)"></div> <div class="left" id="nav-left" onclick="moveReader(false,false)"></div>
<div class="right" id="nav-right" onclick="moveReader(true,false)"></div> <div class="right" id="nav-right" onclick="moveReader(true,false)"></div>
</div> </div>
<div id="help-menu"> <div id="help-menu">
<div id="help-controls" style="opacity:1; transform: translate(0,0);"> <div id="help-controls" style="opacity:1; transform: translate(0,0);">
<div><div class="key">&larr;</div>/ scroll up / clic : previous</div> <div><div class="key">&larr;</div>/ scroll up / clic : previous</div>
@ -52,7 +58,9 @@
<div><div class="key">V</div>: Toggle panel / page viewing mode</div> <div><div class="key">V</div>: Toggle panel / page viewing mode</div>
</div> </div>
</div> </div>
</div> </div>
<div id="reader-progress-container"> <div id="reader-progress-container">
<div id="reader-progress-bar"></div> <div id="reader-progress-bar"></div>
<div id="reader-progress-pages"></div> <div id="reader-progress-pages"></div>
@ -60,10 +68,10 @@
</div> </div>
<!-- End of Melpomene comic reader --> <!-- End of Melpomene comic reader -->
</body> </body>
<!-- melpomene_js.html import --> <!-- melpomene_js.html import -->
<script src="demo_data.js"></script>
<script src="../melpomene.js"></script> <script src="../melpomene.js"></script>
</html> </html>

View File

@ -22,18 +22,19 @@
} }
#reader-pages { #reader-pages {
position: absolute;
flex-direction: row; flex-direction: row;
left: 0px;
right: 0px;
transform-origin: top left; transform-origin: top left;
transform: scale(1) translate(0); transform: scale(1) translate(0);
align-items: center;
width: fit-content;
height: fit-content;
} }
#reader-pages > img { #reader-pages > img {
display: inline-block; display: inline-block;
opacity: 0; opacity: 0;
transition: all 1s cubic-bezier(.9,.03,.69,.22); /* ease-in quartic */ transition: all 1s cubic-bezier(.9,.03,.69,.22); /* ease-in quartic */
flex-shrink: 0;
} }
.fill { .fill {

View File

@ -61,33 +61,6 @@ MOUSEWHELL_WAIT = false
// Zooms utilites // Zooms utilites
// -------------- // --------------
function globalZoomScale(){
if (READER_PAGES.dataset.globalZoomScale != undefined){
return parseFloat(READER_PAGES.dataset.globalZoomScale)
}
return 1.0
}
function globalZoomOffsetX(){
if (READER_PAGES.dataset.globalZoomOffset != undefined){
return parseFloat(READER_PAGES.dataset.globalZoomOffset.split(',')[0])
}
return 0.0
}
function globalZoomOffsetY(){
if (READER_PAGES.dataset.globalZoomOffset != undefined){
return parseFloat(READER_PAGES.dataset.globalZoomOffset.split(',')[1])
}
return 0.0
}
function loadZoomsFromImgTagsIfRequired(){ function loadZoomsFromImgTagsIfRequired(){
// Zooms may be defined by another JS file // Zooms may be defined by another JS file
@ -176,48 +149,37 @@ function getPagesCount() {
return READER_PAGES.childElementCount return READER_PAGES.childElementCount
} }
function pageOriginalHeight(pageNumber) { function pageOriginalHeight() {
return READER_PAGES.children[pageNumber - 1].height return parseInt(READER_PAGES.dataset.pagesHeight)
} }
function pageOriginalWidth(pageNumber) { function pageOriginalWidth() {
return READER_PAGES.children[pageNumber - 1].width return parseInt(READER_PAGES.dataset.pagesWidth)
} }
function readerFrameRatio() { function readerFrameRatio() {
return READER_CONTENT_FRAME.clientWidth / READER_CONTENT_FRAME.clientHeight return READER_CONTENT_FRAME.clientWidth / READER_CONTENT_FRAME.clientHeight
} }
function pageRatio(pageNumber) { function pageRatio() {
return READER_PAGES.children[pageNumber - 1].width / READER_PAGES.children[pageNumber - 1].height return READER_PAGES.dataset.pagesWidth / READER_PAGES.dataset.pagesHeight
} }
function pageMaxHeight(){ function isFrameRatioWiderThanPage(){
let max_height = 0 return readerFrameRatio() > pageRatio()
}
for (var i = 0; i < READER_PAGES.children.length; i++) { function pageToFrameScaleFactor(useHeight){
if(READER_PAGES.children[i].height > max_height){ // The scale factor to apply to a page so it exactly fit in the reader frame
max_height = READER_PAGES.children[i].height if (useHeight) {
} return READER_CONTENT_FRAME.clientHeight / pageOriginalHeight()
} }
return READER_CONTENT_FRAME.clientWidth / pageOriginalWidth()
return max_height
} }
function pageVerticalOffset(pageNumber) { function totalPagesWidth() {
return ( pageMaxHeight() - pageOriginalHeight(pageNumber) ) / 2 // The width of all cumuled pages with scale factor applied
} return pageOriginalWidth() * getPagesCount()
function previousPagesWidth(pageNumber) {
// The width of all previous pages relative to the provided index
let totalWidth = 0
for (let idx = 0; idx < pageNumber - 1; idx++){
totalWidth = totalWidth + READER_PAGES.children[idx].width
}
return totalWidth
} }
// ========= // =========
@ -243,6 +205,7 @@ function initReader(){
for (var i = 0; i < READER_PAGES.children.length; i++) { for (var i = 0; i < READER_PAGES.children.length; i++) {
let img = READER_PAGES.children[i]; let img = READER_PAGES.children[i];
img.style.width = 100 / getPagesCount() + "%"
visibilityObserver.observe(img) visibilityObserver.observe(img)
PROGRESS_BAR_PAGES.appendChild(document.createElement("div")) PROGRESS_BAR_PAGES.appendChild(document.createElement("div"))
@ -263,53 +226,22 @@ function initReader(){
function moveReaderDisplayToArea(pageNumber, width, height, posx, posy){ function moveReaderDisplayToArea(pageNumber, width, height, posx, posy){
// Keep original values for registering // First, scale so the page is at scale 1 compared to the frame
o_width = width READER_PAGES.style.transform = "scale(" + totalPagesWidth() / READER_CONTENT_FRAME.clientWidth + ")"
o_height = height
o_posx = posx
o_posy = posy
// Apply global offsets before scales if we are displaying a zoom // Then, move to the correct page
// Pages display uses width & height = 0 READER_PAGES.style.transform = "translateX(" + (- pageOriginalWidth() * (pageNumber - 1)) + "px )" + READER_PAGES.style.transform
if (width != 0 || height != 0){
width = width * globalZoomScale()
height = height * globalZoomScale()
posx = (posx + globalZoomOffsetX()) * globalZoomScale()
posy = (posy + globalZoomOffsetY()) * globalZoomScale()
}
// reduce width if offset sent us outside of page
if (posx < 0) {
width = width + posx
posx = 0
}
if ((posx + width) > pageOriginalWidth(pageNumber)) {
width = pageOriginalWidth(pageNumber) - posx
}
// reduce height if offset sent us outside of page
if (posy < 0) {
height = height + posy
posy = 0
}
if ((posy + height) > pageOriginalHeight(pageNumber)) {
height = pageOriginalHeight(pageNumber) - posy
}
// Align the top-left corner of the frame with the page
READER_PAGES.style.transform = "translate(-" + previousPagesWidth(pageNumber) + "px, -" + pageVerticalOffset(pageNumber) + "px )"
// Then move so the top-left point of the zoom match the frame top-left // Then move so the top-left point of the zoom match the frame top-left
READER_PAGES.style.transform = "translate(" + (- posx) + "px, " + (-posy) + "px )" + READER_PAGES.style.transform READER_PAGES.style.transform = "translate(" + (- posx) + "px, " + (-posy) + "px )" + READER_PAGES.style.transform
// Then, scale so the zoom would fit the frame, and center the zoom // Then, scale so the zoom would fit the frame, and center the zoom
if (width == 0){ if (width == 0){
width = pageOriginalWidth(pageNumber) width = pageOriginalWidth()
} }
if (height == 0){ if (height == 0){
height = pageOriginalHeight(pageNumber) height = pageOriginalHeight()
} }
zoomRatio = width / height zoomRatio = width / height
@ -340,12 +272,11 @@ function moveReaderDisplayToArea(pageNumber, width, height, posx, posy){
updateFocusByHeight(scaledHeight) updateFocusByHeight(scaledHeight)
} }
// Use values before global offset / scale
CURRENT_PAGE = pageNumber CURRENT_PAGE = pageNumber
CURRENT_WIDTH = o_width CURRENT_WIDTH = width
CURRENT_HEIGHT = o_height CURRENT_HEIGHT = height
CURRENT_X = o_posx CURRENT_X = posx
CURRENT_Y = o_posy CURRENT_Y = posy
} }
function refreshReaderDisplay() { function refreshReaderDisplay() {

View File

@ -69,16 +69,17 @@ def extract_zooms(src_folder):
zooms[idx] = { zooms[idx] = {
"name": svg_path.stem, "name": svg_path.stem,
"width": 0,
"height": 0,
"zooms": [], "zooms": [],
} }
tree = ET.parse(svg_path) tree = ET.parse(svg_path)
root = tree.getroot() root = tree.getroot()
zooms[idx]["width"] = int(root.get("width")) for svg in root.findall('.//{*}svg'):
zooms[idx]["height"] = int(root.get("height")) if area.get("width") > max_width:
max_width = area.get("width")
if area.get("height") > max_width:
max_width = area.get("height")
for area in root.findall('.//{*}rect'): for area in root.findall('.//{*}rect'):
zooms[idx]["zooms"].append([ zooms[idx]["zooms"].append([
@ -119,13 +120,13 @@ def write_html(zooms, dest_file, pages_width, pages_height, prefix, extention):
data_file.write(HTML_START_CONSTANT) data_file.write(HTML_START_CONSTANT)
data_file.write(f' <div id="reader-pages" class="animated" hidden>\n') data_file.write(f' <div id="reader-pages" class="animated" data-pages-width="{pages_width}" data-pages-height="{pages_height}" hidden>\n')
for page_idx in sorted(zooms.keys()): for page_idx in sorted(zooms.keys()):
img_url = f"{prefix}{zooms[page_idx]['name']}.{extention}" img_url = f"{prefix}{zooms[page_idx]['name']}.{extention}"
zoom_html_data = [','.join([str(zoom) for zoom in page_zooms]) for page_zooms in zooms[page_idx]["zooms"]] zoom_html_data = [','.join([str(zoom) for zoom in page_zooms]) for page_zooms in zooms[page_idx]["zooms"]]
zoom_html_str = ';'.join(zoom_html_data) zoom_html_str = ';'.join(zoom_html_data)
data_file.write(f' <img loading="lazy" heigth="{zooms[page_idx]["height"]}" width="{zooms[page_idx]["width"]}" src="{img_url}" data-zooms="{zoom_html_str}"/>\n') data_file.write(f' <img loading="lazy" src="{img_url}" data-zooms="{zoom_html_str}"/>\n')
data_file.write(f' </div>\n') data_file.write(f' </div>\n')