canvas のツール 網目がうまく描けない(未完成)

<!DOCTYPE html>
<meta charset="UTF-8">
<title>HTML Canvas Library</title>
<style>
canvas {
  border: 2px gray ridge;
}
</style>
<body>
<canvas width="500" height="250"></canvas>
<canvas width="500" height="250"></canvas>
<canvas width="500" height="250"></canvas>
<canvas width="500" height="250"></canvas>
<canvas width="500" height="250"></canvas>

<script>

class Canvas {
  #col = 'black'; #view = [ ];

  constructor (canvas, option) {
    this.canvas   = canvas;
    this.option   = Object.assign ({ }, this.constructor.defOption, option);
    this.ctx      = canvas.getContext ('2d');
  }

  cls () {
    this.canvas.width = this.canvas.clientWidth;
    this.viewport (...this.#view);
    return this;
  }

  line (x, y, x1, y1) {
    let { ctx } = this;
    ctx.beginPath ();
      ctx.moveTo (x, y);
      ctx.lineTo (x1, y1);
    ctx.closePath ();
    ctx.stroke ();
    return this;
  }

  viewport (maxX = this.canvas.clientWidth, maxY = this.canvas.clientHeight, minX = 0, minY = 0, deg = 0) {
    const
      { canvas, ctx, option } = this,
      { sin, cos, trunc: int, abs, log10, PI: pi } = Math,
      digitNum = n=> int (log10 (n) + 1);//桁数を求める

    let
      w = canvas.clientWidth,
      h = canvas.clientHeight,
      dx = maxX - minX,
      dy = maxY - minY,
      uw = w / dx,
      uh = h / dy,
      ur = deg * (pi / 180);

    ctx.setTransform (
      uw * cos (ur),
      -sin (ur) * uh,
      -sin (ur) * uw ,
      -uh * cos (ur),
      -minX * uw,
      maxY * uh,
    );

    ctx.lineWidth = 1 / (uw + uh) * 1.;//線の太さは、縦横軸の平均から算出

    if (option.grid) {
      ctx.save ();
        //グリッドの間隔を2点の距離から算出する
        let
          mx = 10 ** (digitNum (uw)),
          my = 10 ** (digitNum (uh)),
          nx = mx / 10,
          ny = my / 10,
          sx = int ((minX - abs (dx)) / nx) * nx,
          ex = int ((maxX + abs (dx)) / nx) * nx,
          sy = int ((minY - abs (dy)) / ny) * ny,
          ey = int ((maxY + abs (dy)) / ny) * ny;

console.log(uw, uh,mx,my, sx,ex,sy,ey);

        this.color = 'silver';
//        for (let p = sx; p < ex; p += nx / 100) this.line (p, sy, p, ey);
//        for (let p = sy; p < ey; p += nx / 100) this.line (sx, p, ex, p);

        this.color = 'silver';
//        for (let p = sx; p < ex; p += nx / 10) this.line (p, sy, p, ey);
//        for (let p = sy; p < ey; p += nx / 10) this.line (sx, p, ex, p);

        this.color = 'gray';
        for (let p = sx; p < ex; p += nx) this.line (p, sy, p, ey);
        for (let p = sy; p < ey; p += ny) this.line (sx, p, ex, p);

        this.color = 'black';
        this.line (0, sy, 0, ey);
        this.line (sx, 0, ex, 0);

      ctx.restore ();
    }
    
    this.#view = [maxX, maxY, minX, minY, deg];
    return this;
  }


  set color (arg) {this.ctx.strokeStyle = this.#col = arg; }
  get color () { return this.#col; }


  static defOption = {
    grid: true,
  }

  static create (canvas = document.querySelector ('canvas'), option = { }) {
    return new this (canvas, option);
  }
}


</script>

ギアにペンを差し込んでグルグルまわるやつ

'''


HTML <a class="keyword" href="http://d.hatena.ne.jp/keyword/canvas">canvas</a>



















  1. -------- Option ---------









'''

<!DOCTYPE html>
<meta charset="UTF-8">
<title>HTML canvas </title>
<style>
ol li label { display: inline-block; width: 10em; padding: 0 1ex; }
ol li { margin: 2px; list-style: none; }
ol li label, ol li input { border: 2px silver ridge; border-radius: 5px; }
</style>
<body>
<form id="STATE">
  <ol>
    <li>
      <label>Aria</label>
      <input type="number" value="1200" step="50" min="50" max="10000">
    <li>
      <label>B:radius</label>
      <input type="number" value="352" min="0" max="5000">
    <li>
      <label>B:offset</label>
      <input type="number" value="0.52" min="0.01" max="1" step=".01">
    <li>
      <label>Line color</label>
      <select>
        <option value="rgba(0,0,0,.3)">Gray</option>
        <option value="rgba(255,0,0,.2)">Red</option>
        <option value="rgba(0,128,0,.2)">Green</option>
        <option value="rgba(0,0,255,.2)">Blue</option>
        <option value="rgba(128,0,255,.2)" selected>Violet</option>
      </select>
    <li>
      <label>Line speed</label>
      <select>
        <option value="1">Slow</option>
        <option value="5">Normal</option>
        <option value="25" selected>Early</option>
        <option value="100">High speed</option>
      </select>
    </li>
    <li>-------- Option ---------
    </li>
    <li>
      <label>C:radius</label>
      <input type="number" value="1" min="1" max="20">
    <li>
      <label>C:offset</label>
      <input type="number" value="0" min="0" max="1" step=".03" max="1">
  </ol>
</form>
<canvas width="1200" height="1200"></canvas>

<script>

//-----------------------------------------------

class Canvas {
  constructor (e, area, color = 'rgba(0,0,0,.5)') {
    this.canvas = e;
    this.ctx = e.getContext ('2d');
    this.color = color;
    this.area = area;
    this.m = null;
  }

  cls () {
    let { ctx, canvas } = this;
    ctx.clearRect (0, 0, canvas.width, canvas.height);
    return this;
  }

  set area (p) {
    if (p) {
      let { ctx, canvas } = this;
      [canvas.width, canvas.height] = p.value;
      this.m = null;
      ctx.translate (canvas.offsetWidth / 2, canvas.offsetHeight / 2);
    }
  }

  set color (rgba) {
    this.ctx.strokeStyle = rgba;
  }

  line (p) {
    let {ctx, m} = this;
    if (null === m) {
      ctx.moveTo (...p.value);      
    }
    else {
      ctx.beginPath ();
      ctx.moveTo (...m.value);
      ctx.lineTo (...p.value);
      ctx.stroke ();
    }
    this.m = p;
    return this;
  }
}

//-----------------------------------------------

class P {
  #p = [ ];
  constructor (a) { this.#p = a; }
  add (q) { return new this.constructor (q.value.map ((a,i)=> this.#p[i]+a)) }
  sub (q) { return new this.constructor (q.value.map ((a,i)=> this.#p[i]-a)) }
  get value () { return this.#p }
  set value (p) { this.#p = p }
}

//-----------------------------------------------

class Circle {
  #rad = null; //ラジアン
  #c = null; //円周

  constructor (radius = 1, offset = 1/* (%) */, rad = 0) {
    this.radius = radius;
    this.offset = offset;
    this.#rad = rad;
    this.#c = 2 * Math.PI * radius;
  }

  rotation (n) {
    this.#rad += n / this.#c;
    return this.position;
  }

  get position () {
    let r = this.radius *  this.offset;
    let rad = this.#rad;
    return new P ([Math.sin (rad) * r, Math.cos (rad) * r]);
  }
}

//-----------------------------------------------

let
  CG = new Canvas (document.querySelector ('canvas')),
  C0 = null,
  C1 = null,
  C2 = null,
  ANIME_ID = null,
  PM = [...document.querySelectorAll ('form input[type="number"], form select')],
  STEP = 25,
  SPEED = null;
  

function draw () {
  for (let i = SPEED; i--; )
    CG.line (C0.rotation (STEP).add (C1.rotation (-STEP)).add(C2.rotation(-STEP)));
  requestAnimationFrame (draw);
}

//-----------------------------------------------

function demo () {
  if (ANIME_ID)
    cancelAnimationFrame (ANIME_ID);

  let [a, r, offset, col, sp, gr, go] = PM.map (e=> 'valueAsNumber' in e ? e.valueAsNumber: e.value);
  let r0 = a/2*.95 - ( r * offset);
  CG.area = new P ([a, a]);
  CG.color = col;
  SPEED = +sp;

  C0 = new Circle (r0, 1),
  C1 = new Circle (r, offset);
  C2 = new Circle (gr, go);

  draw ();
}

//-----------------------------------------------

function hndler () { demo (); }
STATE.addEventListener ('change', hndler, false);

demo ();

</script>
    

www.amazon.co.jp

location.hash と webapl を連動させるライブラリ

Ajax を利用してアプリを作るとき、location.hash の値と連動して動く。
onHashChange を使用せずに済む。


とある条件でページをスクリプトで生成するプログラムがあるとする。
その条件が変化するごとに location.hash に書き出し、コールバック(生成プログラム)に条件を渡して呼び出す
外部からある条件下でページを生成したい場合 location.hash に条件を書いて呼び出す
インスタンス化した時点の条件を標準とし、それと違う用件だけを location.hash に記述する

2022-07-09 更新

/*----------------------------------------------
  onHashChange を使用せず
  現在の状態を location.hash に反映させる(逆も可)
  対象となるFORM の要素群には id もしくは name 属性が必要である
----------------------------------------------*/

class ApplicationNavigator {
  
  #defaultState = null;//初期設定時の状態を基本とする

  constructor (form = document.querySelector ('form'), cbFunc = null, cbObj = null, ...cbArgs) {
    this.form = form;
    this.cbFunc = cbFunc;
    this.cbObj = cbObj;
    this.cbArgs = cbArgs;
    this.elements = [...form.querySelectorAll ('input[id], input[name], select[id], select[name], textarea[id], textarea[name]')];
    this.#defaultState = this.constructor.getValues (this.elements);
    this.hash = location.hash;

    if (cbFunc)
      form.addEventListener ('change', this, false);
  }


  execute (e) {
    if ('function' === typeof this.cbFunc) {
      this.cbFunc.apply (this.cbObj || this, [this.status, ...this.cbArgs]);
      location.hash = this.hash; 
    }
    return this;
  }


  reset () {
    this.status = this.#defaultState;
    return this;
  }


  handleEvent (event) {
    let e = event.target;
    if (this.elements.includes (e))
      this.execute (e);
  }


  get status () {//全ての状態を返す
    return this.constructor.getValues (this.elements);
  }

  set status (obj = { }) {//statusには実態がない(その都度要素から収集)
    this.constructor.setValues (this.elements, Object.assign ({ }, obj));
    this.execute ();//コールバック関数を呼び出す
  }


  get hash () {
    let a = this.#defaultState;
    return '#' +
      Object.entries (this.status)
        .filter (([k,v])=> a[k] != v)
        .map (b=> b.join`=`)
        .join('&');
  }
  set hash (hash) {
     this.status = this.constructor.parseParms (hash);
  }


  //______________________


  static parseParms (str = '') {
    let
      reg = /(?:^#)?(\D\w*)(\[\])?=(.*?)(?:\&|\;|$)/g,
      pieces = new Map,
      piece;

    while (piece = reg.exec (str)) {
      let [, key, isAry, val] = piece;
      key = decodeURIComponent (key);
      val = decodeURIComponent (val);

      if (isAry)
        val = [val];

      if (pieces.has (key))
        pieces.set (key, [].concat (pieces.get (key), val));
      else
        pieces.set (key, val);
    }

    return Object.fromEntries (pieces);
  }


  //要素の値を取得してオブジェクトにして返す
  //name属性が複数な場合配列として記憶する
  static getValues ([...es]) {
    const
      excludeType = ['fieldset', 'reset', 'submit', 'image', 'file','button'],//除外する要素
      obj = new Map;

    for (let e of es) {
      let type = e.type, key, value;

      if (excludeType.includes (type))
        continue;
      if (['checkbox', 'radio'].includes (type) && !e.checked)
        continue;//checked属性がfalsse は除外する

      if (key = e.id || e.name) {//keyがあるものだけ有効
        //最初から配列とする
        if ('select-multiple' === type)
          value = [...e.options].reduce ((a, b)=> (b.selected && a.push (b.value), a), [ ]);
        else if ('checkbox' === type)
          value = [value];
        else
          value = e.value;

        if (obj.has (key))//すでに登録されたものは配列
          value = [].concat (obj.get (key), value);

        obj.set (key, value);
      }
    }
    return Object.fromEntries (obj);//オブジェクト化
  }


  //指定要素群に値を代入する
  static setValues ([...es], obj = { }) {
    const
      priority = ['radio', 'checkbox', 'select-one', 'select-multiple'],//優先順位
      prioritize = (a, b)=> priority.indexOf (a.type) - priority.indexOf (b.type);

    let hs = es.reduce ((a,e)=> (a[e.id||e.name] = (a[e.id||e.name] || []).concat(e), a), { });//名前毎にhashにする

    //名前毎の中で優先順に並び替え
    Object.values (hs).forEach (v=> v.sort (prioritize));
    //select要素のmultipleのすべてをリセットする
    es.filter (e=> e.type === 'select-multiple').flatMap (e=> [...e.options]).forEach (e=> e.selected = false);
    //es.filter (e=> e.hasAttribute ('defaulValue')).forEach (e=> e.value = e.defaultValue);
      
    for (let [name, vals] of Object.entries (obj)) {//※valsはコピーしたもの
      let es = hs[name] || [ ];
      vals = [].concat (vals);

      while (vals.length) {
        let val = vals.shift ();
        for (let e of es) {
          switch (e.type) {
            //想定のtype順で設定する
            case 'select-multiple':
              for (let op of e.options) {
                if (op.value == val) {
                  op.selected = true;
                  break;
                }
              }
              break;
            case 'select-one' :  e.value = val; break;
            case 'checkbox' : case 'radio' : e.checked = e.value === val; break;
            default : e.value = val; break;
          }
        }
      }
    }
  }
}


location.hash を解析して object にして返す

正規表現を使うのは大げさな気がする。
location.hash を location.search に書き換えて URL.searchParams で取得するほうが簡単だろうか?

[, 変数名, 大括弧, 変数値]に分割しながら解析する

function parseParms (str = '') {
  let
    reg = /(?:^#)?(\D\w*)(\[\])?=(.*?)(?:\&|\;|$)/g,
    pieces = new Map;

  while (piece = reg.exec (str)) {
    let [, key, isAry, val] = piece;
    key = decodeURIComponent (key);
    val = decodeURIComponent (val);

    if (isAry)
      val = [val];

    if (pieces.has (key))
      pieces.set (key, [].concat (pieces.get (key), val));
    else
      pieces.set (key, val);
  }

  return Object.fromEntries (pieces);
}

同一人物だろっ!

質問者のこいつらは同一人物だ!
回答するのを止めた。

  • goo では名前を漢字で、okwaveでは2つの単語をアンダースコアでつなぎ、寺尾では2つの単語をつなぐ。
  • 回答したコードを精査しないで、そのまま引用して次の質問する。
  • 自分のやりたいことだけが優先されてコードにその努力が見えない。
  • 過去の経験からこの手の奴は、どのサイトでも複数のアカウントを所持している。(憶測)

インデントを波型にして回答してみようか?

2段階プルダウンで表示内容を切り替えたい

2段階プルダウンで表示内容を切り替えたい
2段階プルダウンで1段階目の選択を終えた後に選択ボックスを見えなくしたい

goo: https://oshiete.goo.ne.jp/qa/13030027.html
okwave:https://okwave.jp/qa/q10026854.html

無駄に答えてしまった。phpのプログラムは評価しづらい。

<!DOCTYPE html>
<meta charset="utf-8">
<title></title>
<style>
</style>
      
<body>
<p>
  <select id="Y"></select> /
  <select id="M"></select> /
  <select id="D"></select>
</p>
<table border="1" id="T"></table>


<script>

async function ajax (path, args = { }, cbFunc = null, ...cbArgs) {
  let res = await fetch (path, { method: 'post', body: JSON.stringify (args) });
  let txt = await res.text ();
  let json = JSON.parse(txt);
  return 'function' === typeof cbFunc ? cbFunc.call (...cbArgs): json;
}

function ary2tbody (a,b=document.createElement('tbody')){a.reduce((b,a)=>(a.reduce((c,d)=>(c.insertCell().append(d),c),b.insertRow()),b),b)}
function replaceOptions (e, ary) {e.replaceChildren (...ary.map (a=> new Option (...a)))}

function replaceY (y=(new Date).getFullYear(),n=10) {replaceOptions (Y,[...Array(n)].map ((_,i)=>[y-i]))}
function replaceM () {replaceOptions (M,[...Array(12)].map ((_,i)=>[i+1]))}
function replaceD (y=Y.value,m=M.value) { replaceOptions (D,[...Array((new Date (y,m,0)).getDate())].map ((_,i)=>[i+1]))}

async function loadContent (y=Y.value, m=M.value, d=D.value) {
  ary2tbody(await ajax ('test.php', {date: `${y}-${m}-${d}`}), T);
}

function handler (event) {
  let e = event.target;
  switch (true) {
    case M == e : replaceD (); break;
    case D == e : loadContent (); break;
  }
}

document.addEventListener ('change', handler, false);
[replaceY,replaceM,replaceD].map(f=>f());


</script>

test.php

<?php
const MySQL_DBNAME = '********';
const MySQL_HOST   = 'localhost';
const MySQL_USER   = '********';
const MySQL_PASS   = '********';

$db = sprintf ('mysql:dbname=%s; host=%s; charset=utf8mb4', MySQL_DBNAME, MySQL_HOST);
$mysql = new PDO ($db, MySQL_USER, MySQL_PASS);
$query = 'select * from table_name where  :date < date';//ここは適当に
$args = json_decode (file_get_contents ('php://input'), true);

$hd = $mysql-> prepare ($query);
$hd-> execute ($args);
$result = $hd-> fetchAll ( PDO::FETCH_NUM);

header ('Content-type: application/json; charset=utf-8');
header ('X-Content-Type-Options: nosniff');

echo json_encode ($result);//JSONに変換する
?>