tongong.net

personal website
git clone https://tongong.net/git/tongong.net.git
Log | Files | Refs

zoomimg.js (5256B)


      1 const m = require("lib/mithril");
      2 const stream = require("lib/mithril-stream");
      3 const sthelp = require("./modules/stream-helpers.js");
      4 
      5 // size: number
      6 //   size in pixels (screen not css)
      7 // image: (x, y) -> pixel-value
      8 //   0 <= x, y, < 1
      9 //   transform a position in the image to a local state value like a color.
     10 //   do not fluctuate locally (image and noise arguments are somewhat similar
     11 //   to vertex and fragment shader)
     12 // alternatively image can be an Image object. then pixel-value for the noise
     13 //   function will be [r, g, b]
     14 // noise: (x, y, pixel-value) -> [r, g, b]
     15 //   0 <= r, g, b <= 255
     16 //   take pixel coordinates and the value generated by image() and transform
     17 //   them to rgb values
     18 // update: stream
     19 //   set zoom level by stream value
     20 module.exports = () => {
     21     let sliderupdate;
     22     let zoom;
     23     return {
     24         oninit: (vnode) => {
     25             zoom = stream(1);
     26             sliderupdate = stream();
     27         },
     28         oncreate: (vnode) => {
     29             const canvas = vnode.dom.querySelector("canvas");
     30             const input_rough = vnode.dom.querySelector("input#rough")
     31             const input_fine = vnode.dom.querySelector("input#fine")
     32 
     33             input_rough.value = 1;
     34             input_fine.value = 0;
     35 
     36             sliderupdate.map(_ =>
     37                 parseFloat(input_rough.value)
     38                 + 0.01 * parseFloat(input_fine.value)
     39             ).map(val => zoom(Math.min(Math.max(val, 0), 2)));
     40 
     41             if (vnode.attrs.update) vnode.attrs.update.map(zoom);
     42 
     43             // only update sliders on external zoom set (sliders do not need to
     44             // update themselves)
     45             if (vnode.attrs.update) vnode.attrs.update.map(val => {
     46                 input_rough.value = Math.round(val * 100) / 100;
     47                 input_fine.value = (val - input_rough.value) * 100;
     48             });
     49 
     50             zoom.map(z => canvas.style.transform =
     51                 "translate(-50%, -50%) scale(" + z + ")"
     52             );
     53 
     54             let SIZE = vnode.attrs.size;
     55             let SIZE_CSS = SIZE / window.devicePixelRatio;
     56             canvas.style.width = canvas.style.height = SIZE_CSS + "px";
     57             canvas.width = canvas.height = SIZE;
     58 
     59             const ctx = canvas.getContext("2d");
     60             let image_data;
     61             if (vnode.attrs.image instanceof HTMLImageElement) {
     62                 ctx.drawImage(vnode.attrs.image, 0, 0);
     63                 image_data = ctx.getImageData(0, 0, SIZE, SIZE);
     64             } else {
     65                 image_data = ctx.createImageData(SIZE, SIZE);
     66             }
     67             for (let x = 0; x < SIZE; x++) {
     68                 for (let y = 0; y < SIZE; y++) {
     69                     const pos = (y * SIZE + x) * 4;
     70                     let val;
     71                     if (vnode.attrs.image instanceof HTMLImageElement) {
     72                         val = image_data.data.slice(pos, pos + 3)
     73                     } else {
     74                         val = vnode.attrs.image(x / SIZE, y / SIZE);
     75                     }
     76                     [
     77                         image_data.data[pos],
     78                         image_data.data[pos + 1],
     79                         image_data.data[pos + 2],
     80                     ] = vnode.attrs.noise(x, y, val);
     81                     image_data.data[pos + 3] = 255;
     82                 }
     83             }
     84             ctx.putImageData(image_data, 0, 0);
     85         },
     86         view: (vnode) => [
     87             m("div",
     88                 m("div", { style: {
     89                     position: "relative",
     90                     overflow: "hidden",
     91                     lineHeight: 0,
     92                     width: "100%",
     93                     paddingBottom: "100%",
     94                 }},
     95                     m("div", { style: {
     96                         position: "absolute",
     97                         width: "100%",
     98                         height: "100%",
     99                     }},
    100                         m("canvas", { style: {
    101                             position: "absolute",
    102                             top: "50%",
    103                             left: "50%",
    104                             transform: "",
    105                         }})
    106                     )
    107                 ),
    108                 m("p"),
    109                 m("input#rough", {
    110                     type: "range",
    111                     min: 0,
    112                     max: 2,
    113                     step: 0.01,
    114                     oninput: () => sliderupdate(0),
    115                 }),
    116                 m("input#fine", {
    117                     type: "range",
    118                     min: -1,
    119                     max: 1,
    120                     step: 0.01,
    121                     oninput: () => sliderupdate(0),
    122                 }),
    123                 m("p", m("i",
    124                     vnode.attrs.size + "x" + vnode.attrs.size + " - " +
    125                     "Zoom: " + zoom().toFixed(4) + "x - ",
    126                     m("a", {
    127                         onclick: () => {
    128                             const canvas = vnode.dom.querySelector("canvas");
    129                             canvas.toBlob(blob => {
    130                                 const url = URL.createObjectURL(blob);
    131                                 window.open(url, '_blank');
    132                             });
    133                         }
    134                     }, "open image")
    135                 )),
    136             ),
    137         ],
    138     };
    139 };