{"id":5883,"date":"2026-02-03T10:00:36","date_gmt":"2026-02-03T01:00:36","guid":{"rendered":"https:\/\/comcent.co.jp\/blog\/?p=5883"},"modified":"2026-01-27T17:15:58","modified_gmt":"2026-01-27T08:15:58","slug":"javascript-%e3%81%a7%e3%82%ab%e3%83%bc%e3%82%bd%e3%83%ab%e3%82%92%e8%bf%bd%e3%81%84%e3%81%8b%e3%81%91%e3%81%a6%e5%8b%95%e3%81%8f%e8%a6%81%e7%b4%a0%e3%82%92%e4%bd%9c%e3%82%8b","status":"publish","type":"post","link":"https:\/\/comcent.co.jp\/blog\/archives\/5883\/","title":{"rendered":"JavaScript \u3067\u30ab\u30fc\u30bd\u30eb\u3092\u8ffd\u3044\u304b\u3051\u3066\u52d5\u304f\u8981\u7d20\u3092\u4f5c\u308b"},"content":{"rendered":"\n<p>\u3072\u3068\u6614\u306f\u30ab\u30fc\u30bd\u30eb\u3092\u30b5\u30a4\u30c8\u72ec\u81ea\u306e\u30a2\u30a4\u30b3\u30f3\u306b\u5909\u3048\u305f\u308a\u3057\u305f\u30b5\u30a4\u30c8\u3092\u898b\u304b\u3051\u305f\u3082\u306e\u3067\u3059\u304c\u3001\u3055\u3089\u306b\u51dd\u3063\u305f\u3082\u306e\u3068\u3057\u3066\u30ab\u30fc\u30bd\u30eb\u3092\u30a2\u30a4\u30b3\u30f3\u306a\u3069\u304c\u8ffd\u3044\u304b\u3051\u3066\u304f\u308b\u3088\u3046\u306a\u6f14\u51fa\u3082\u898b\u304b\u3051\u305f\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002\u3061\u3087\u3046\u3069\u30b2\u30fc\u30e0\u3067\u4e3b\u4eba\u516c\u3092\u8ffd\u3044\u304b\u3051\u3066\u304f\u308b\u8ffd\u5c3e\u5f3e\u306e\u3088\u3046\u306a\u3001\u3042\u306e\u52d5\u304d\u3067\u3059\u3002<\/p>\n\n\n\n<p>\u6700\u8fd1\u3042\u307e\u308a\u7d20\u306e JavaScript \u3092\u66f8\u304f\u6a5f\u4f1a\u304c\u306a\u304b\u3063\u305f\u306e\u3067\u3001\u982d\u306e\u4f53\u64cd\u304c\u3066\u3089 AI \u306b\u983c\u3089\u305a\u3042\u306e\u52d5\u304d\u3092\u5b9f\u88c5\u3057\u3066\u307f\u307e\u3057\u305f\u3002<\/p>\n\n\n\n<p>\u63a2\u305b\u3070\u7d76\u5bfe\u306b\u4fbf\u5229\u306a\u30e9\u30a4\u30d6\u30e9\u30ea\u304c\u3042\u308b\u306f\u305a\u3067\u3059\u304c\u3001\u4eca\u56de\u306f\u30b3\u30d4\u30da\u3067\u6e08\u3080\u3088\u3046\u306b\u7d20\u306e JavaScript \u3067\u5b9f\u88c5\u3057\u3066\u307f\u307e\u3059\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-comcent-blog-headline01 modHeadline\">\u304a\u6025\u304e\u306e\u65b9\u3078<\/h4>\n\n\n\n<p>\u4ee5\u4e0b\u306e class \u304c\u3044\u308f\u3086\u308b\u30e9\u30a4\u30d6\u30e9\u30ea\u90e8\u5206\u3067\u3059\u3002\u3053\u308c\u3092\u307e\u308b\u3054\u3068\u30b3\u30d4\u30da\u3057\u3066\u3001\u81ea\u8eab\u306e HTML \u306b\u7d44\u307f\u8fbc\u3093\u3067\u304f\u3060\u3055\u3044\u3002&nbsp;\u30b3\u30d4\u30da\u3057\u305f\u5f8c\u306e\u4f7f\u3044\u65b9\u306f\u3059\u3050\u4e0b\u306e\u4e00\u756a\u5358\u7d14\u306a\u7528\u4f8b\u3092\u53c2\u8003\u306b\u3057\u3066\u4e0b\u3055\u3044\u3002<\/p>\n\n\n\n<textarea style=\"border:1px solid #eee;padding:10px;width:100%;min-height:300px;\">\n  class Tracing {\n        \/\/ 1mm \u79d2\u9593\u306b\u8ffd\u8de1\u7269\u304c\u9032\u3080\u8ddd\u96e2 (px)\n        #speed;\n\n        \/\/ \u524d\u56de\u306e\u30eb\u30fc\u30d7\u5b9f\u884c\u6642\u9593\n        #beforeTimestamp = 0;\n\n        \/\/ \u8ffd\u8de1\u7269\u3068\u305d\u306e\u5ea7\u6a19\u914d\u5217\n        #tracers = new Map();\n\n        \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306e\u73fe X \u5ea7\u6a19\n        #targetX = 0;\n\n        \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306e\u73fe Y \u5ea7\u6a19\n        #targetY = 0;\n\n        \/\/ \u53cd\u767a\u3055\u305b\u308b\u304b\u3069\u3046\u304b\n        #repulsion = false;\n\n        \/\/ \u53cd\u767a\u3055\u305b\u308b\u8ddd\u96e2\u306e\u95be\u5024\n        #maxDistance = 9999;\n\n        \/\/ \u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u30eb\u30fc\u30d7\u306e\u958b\u59cb\n        constructor(speed) {\n          const loop = () => {\n            \/\/ \u73fe\u5728\u306e\u6642\u9593\u3092\u53d6\u5f97\n            const timestamp = Date.now();\n\n            \/\/ \u524d\u56de\u306e\u30eb\u30fc\u30d7\u304b\u3089\u306e\u7d4c\u904e\u6642\u9593\u3092\u8a08\u7b97\n            const delta = timestamp - this.#beforeTimestamp;\n\n            \/\/ \u4eca\u56de\u306e\u30eb\u30fc\u30d7\u6642\u9593\u3092\u4fdd\u5b58\n            this.#beforeTimestamp = timestamp;\n\n            \/\/ \u8ffd\u8de1\u7269\u3092\u3059\u3079\u3066\u66f4\u65b0\n            this.#tracers.forEach((parameter) => {\n              \/\/ \u8ffd\u8de1\u7269\u306e\u73fe\u5728\u4f4d\u7f6e\u3092\u53d6\u5f97\n              const {x: tracerX, y: tracerY, listener} = parameter;\n\n              \/\/ \u8ffd\u8de1\u7269\u3068\u8ffd\u8de1\u5bfe\u8c61\u306e\u8ddd\u96e2\u3092\u8a08\u7b97\n              const dx = this.#targetX - tracerX;\n              const dy = this.#targetY - tracerY;\n              const distance = Math.sqrt(dx * dx + dy * dy);\n\n              \/\/ 1 \u30d5\u30ec\u30fc\u30e0\u3067\u9032\u3080\u8ddd\u96e2\u3092\u8a08\u7b97\n              const distancePerFrame = this.#speed * delta;\n\n              \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306b\u5411\u304b\u3046\u5358\u4f4d\u30d9\u30af\u30c8\u30eb\u3092\u8a08\u7b97\n              if (distance > distancePerFrame) {\n                \/\/ \u53cd\u767a\u3055\u305b\u308b\u5834\u5408\u3001\u95be\u5024\u4ee5\u5185\u306a\u3089\u8ffd\u8de1\u5bfe\u8c61\u304b\u3089\u9060\u3056\u3051\u308b\n                const newX = tracerX + dx \/ distance * distancePerFrame * (this.#repulsion && distance < this.#maxDistance ? -1 : 1);\n                const newY = tracerY + dy \/ distance * distancePerFrame * (this.#repulsion && distance < this.#maxDistance ? -1 : 1);\n\n                \/\/ \u8ffd\u8de1\u7269\u306e\u4f4d\u7f6e\u3092\u66f4\u65b0\n                parameter.x = newX;\n                parameter.y = newY;\n\n                \/\/ \u30ea\u30b9\u30ca\u30fc\u95a2\u6570\u3092\u547c\u3073\u51fa\u3057\n                listener(newX, newY);\n              }\n\n              \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306b\u76f4\u63a5\u79fb\u52d5\n              else {\n                parameter.x = this.#targetX;\n                parameter.y = this.#targetY;\n\n                \/\/ \u30ea\u30b9\u30ca\u30fc\u95a2\u6570\u3092\u547c\u3073\u51fa\u3057\n                listener(this.#targetX, this.#targetY);\n              }\n            });\n\n            \/\/ \u6b21\u306e\u30eb\u30fc\u30d7\u3092\u4e88\u7d04\n            requestAnimationFrame(loop);\n          };\n\n          \/\/ \u30b9\u30d4\u30fc\u30c9\u306e\u8a2d\u5b9a\n          this.#speed = speed;\n\n          \/\/ \u30eb\u30fc\u30d7 1 \u56de\u76ee\u304c\u53c2\u7167\u3059\u308b\u6642\u9593\u3092\u8a2d\u5b9a\n          this.#beforeTimestamp = Date.now();\n\n          \/\/ requestAnimationFrame \u3067\u30eb\u30fc\u30d7\u3092\u958b\u59cb\n          requestAnimationFrame(loop);\n        }\n\n        \/\/ 1mm \u79d2\u9593\u306b\u8ffd\u8de1\u7269\u304c\u9032\u3080\u8ddd\u96e2 (px) \u3092\u8a2d\u5b9a\n        setSpeed(speed) {\n          this.#speed = speed;\n        }\n\n        \/\/ \u8ffd\u8de1\u7269\u3068\u3001\u6b21\u306e\u30d5\u30ec\u30fc\u30e0\u3067\u305d\u306e\u8ffd\u8de1\u7269\u304c\u79fb\u52d5\u3059\u308b\u3079\u304d\u5ea7\u6a19\u3092\u5f15\u6570\u306b\u53d6\u308b\u95a2\u6570\u3092\u767b\u9332\n        setTracer(element, listener, x = 0, y = 0) {\n          this.#tracers.set(element, {listener, x, y});\n        }\n\n        \/\/ \u8ffd\u8de1\u7269\u306e\u73fe\u5728\u4f4d\u7f6e\u3092\u8a2d\u5b9a\n        setTracerPosition(element, x, y, relative = false) {\n          const tracer = this.#tracers.get(element);\n          if (tracer) {\n            tracer.x = relative ? tracer.x + x : x;\n            tracer.y = relative ? tracer.y + y : y;\n          }\n        }\n\n        \/\/ \u767b\u9332\u3055\u308c\u3066\u3044\u308b\u8ffd\u8de1\u7269\u3092\u524a\u9664\n        removeTracer(element) {\n          this.#tracers.delete(element);\n        }\n\n        \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306e\u5ea7\u6a19\u3092\u66f4\u65b0\n        setTarget(x, y) {\n          this.#targetX = x;\n          this.#targetY = y;\n        }\n\n        \/\/ \u53cd\u767a\u3055\u305b\u308b\u304b\u3069\u3046\u304b\u3092\u53d6\u5f97\n        getRepulsion() {\n          return this.#repulsion;\n        }\n\n        \/\/ \u53cd\u767a\u3055\u305b\u308b\u304b\u3069\u3046\u304b\u3092\u8a2d\u5b9a\n        setRepulsion(repulsion) {\n          this.#repulsion = repulsion;\n        }\n\n        \/\/ \u53cd\u767a\u3055\u305b\u308b\u8ddd\u96e2\u306e\u95be\u5024\u3092\u8a2d\u5b9a\n        setMaxDistance(maxDistance) {\n          this.#maxDistance = maxDistance;\n        }\n      };\n<\/textarea>\n\n\n\n<h4 class=\"wp-block-comcent-blog-headline01 modHeadline\">\u4e00\u756a\u5358\u7d14\u306a\u7528\u4f8b<\/h4>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1584\" height=\"1012\" loading=\"lazy\" src=\"https:\/\/comcent.co.jp\/blog\/wp-content\/uploads\/2025\/10\/noji-2025-10-01-blog-0002.gif\" alt=\"\" class=\"wp-image-5885\"\/><\/figure>\n\n\n\n<p>\u4ee5\u4e0b\u306f\u3001\u30ab\u30fc\u30bd\u30eb\u3092\u8ffd\u3044\u304b\u3051\u308b\u8d64\u3044\u5186\u3092\u8868\u793a\u3059\u308b HTML \u306e\u4f8b\u3067\u3059\u3002<\/p>\n\n\n\n<pre class=\"wp-block-preformatted modBlobCode\">&lt;!doctype html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>  &lt;meta charset=\"utf-8\"&gt;<br>  &lt;title&gt;\u30ab\u30fc\u30bd\u30eb\u8ffd\u8de1 DEMO&lt;\/title&gt;<br>  &lt;style&gt;<br>      .circle {<br>          transition: top, left 0.1s linear;<br>          position: absolute;<br>          width: 50px;<br>          height: 50px;<br>          border-radius: 50%;<br>          pointer-events: none; \/* \u30af\u30ea\u30c3\u30af\u3092\u7121\u8996 *\/<br>          transform: translate(-50%, -50%); \/* \u4e2d\u5fc3\u306b\u914d\u7f6e *\/<br>      }<br>  &lt;\/style&gt;<br>&lt;\/head&gt;<br>&lt;body&gt;<br>&lt;h1&gt;\u30ab\u30fc\u30bd\u30eb\u8ffd\u8de1 DEMO&lt;\/h1&gt;<br>&lt;div id=\"circle1\" class=\"circle\" style=\"background-color: red;\"&gt;&lt;\/div&gt;<br>&lt;script defer&gt;<br>  class Tracing {<br>    \/\/ 1mm \u79d2\u9593\u306b\u8ffd\u8de1\u7269\u304c\u9032\u3080\u8ddd\u96e2 (px)<br>    #speed;<br><br>    \/\/ \u524d\u56de\u306e\u30eb\u30fc\u30d7\u5b9f\u884c\u6642\u9593<br>    #beforeTimestamp = 0;<br><br>    \/\/ \u8ffd\u8de1\u7269\u3068\u305d\u306e\u5ea7\u6a19\u914d\u5217<br>    #tracers = new Map();<br><br>    \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306e\u73fe X \u5ea7\u6a19<br>    #targetX = 0;<br><br>    \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306e\u73fe Y \u5ea7\u6a19<br>    #targetY = 0;<br><br>    \/\/ \u53cd\u767a\u3055\u305b\u308b\u304b\u3069\u3046\u304b<br>    #repulsion = false;<br><br>    \/\/ \u53cd\u767a\u3055\u305b\u308b\u8ddd\u96e2\u306e\u95be\u5024<br>    #maxDistance = 9999;<br><br>    \/\/ \u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u30eb\u30fc\u30d7\u306e\u958b\u59cb<br>    constructor(speed) {<br>      const loop = () =&gt; {<br>        \/\/ \u73fe\u5728\u306e\u6642\u9593\u3092\u53d6\u5f97<br>        const timestamp = Date.now();<br><br>        \/\/ \u524d\u56de\u306e\u30eb\u30fc\u30d7\u304b\u3089\u306e\u7d4c\u904e\u6642\u9593\u3092\u8a08\u7b97<br>        const delta = timestamp - this.#beforeTimestamp;<br><br>        \/\/ \u4eca\u56de\u306e\u30eb\u30fc\u30d7\u6642\u9593\u3092\u4fdd\u5b58<br>        this.#beforeTimestamp = timestamp;<br><br>        \/\/ \u8ffd\u8de1\u7269\u3092\u3059\u3079\u3066\u66f4\u65b0<br>        this.#tracers.forEach((parameter) =&gt; {<br>          \/\/ \u8ffd\u8de1\u7269\u306e\u73fe\u5728\u4f4d\u7f6e\u3092\u53d6\u5f97<br>          const {x: tracerX, y: tracerY, listener} = parameter;<br><br>          \/\/ \u8ffd\u8de1\u7269\u3068\u8ffd\u8de1\u5bfe\u8c61\u306e\u8ddd\u96e2\u3092\u8a08\u7b97<br>          const dx = this.#targetX - tracerX;<br>          const dy = this.#targetY - tracerY;<br>          const distance = Math.sqrt(dx * dx + dy * dy);<br><br>          \/\/ 1 \u30d5\u30ec\u30fc\u30e0\u3067\u9032\u3080\u8ddd\u96e2\u3092\u8a08\u7b97<br>          const distancePerFrame = this.#speed * delta;<br><br>          \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306b\u5411\u304b\u3046\u5358\u4f4d\u30d9\u30af\u30c8\u30eb\u3092\u8a08\u7b97<br>          if (distance &gt; distancePerFrame) {<br>            \/\/ \u53cd\u767a\u3055\u305b\u308b\u5834\u5408\u3001\u95be\u5024\u4ee5\u5185\u306a\u3089\u8ffd\u8de1\u5bfe\u8c61\u304b\u3089\u9060\u3056\u3051\u308b<br>            const newX = tracerX + dx \/ distance * distancePerFrame * (this.#repulsion &amp;&amp; distance &lt; this.#maxDistance ? -1 : 1);<br>            const newY = tracerY + dy \/ distance * distancePerFrame * (this.#repulsion &amp;&amp; distance &lt; this.#maxDistance ? -1 : 1);<br><br>            \/\/ \u8ffd\u8de1\u7269\u306e\u4f4d\u7f6e\u3092\u66f4\u65b0<br>            parameter.x = newX;<br>            parameter.y = newY;<br><br>            \/\/ \u30ea\u30b9\u30ca\u30fc\u95a2\u6570\u3092\u547c\u3073\u51fa\u3057<br>            listener(newX, newY);<br>          }<br><br>          \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306b\u76f4\u63a5\u79fb\u52d5<br>          else {<br>            parameter.x = this.#targetX;<br>            parameter.y = this.#targetY;<br><br>            \/\/ \u30ea\u30b9\u30ca\u30fc\u95a2\u6570\u3092\u547c\u3073\u51fa\u3057<br>            listener(this.#targetX, this.#targetY);<br>          }<br>        });<br><br>        \/\/ \u6b21\u306e\u30eb\u30fc\u30d7\u3092\u4e88\u7d04<br>        requestAnimationFrame(loop);<br>      };<br><br>      \/\/ \u30b9\u30d4\u30fc\u30c9\u306e\u8a2d\u5b9a<br>      this.#speed = speed;<br><br>      \/\/ \u30eb\u30fc\u30d7 1 \u56de\u76ee\u304c\u53c2\u7167\u3059\u308b\u6642\u9593\u3092\u8a2d\u5b9a<br>      this.#beforeTimestamp = Date.now();<br><br>      \/\/ requestAnimationFrame \u3067\u30eb\u30fc\u30d7\u3092\u958b\u59cb<br>      requestAnimationFrame(loop);<br>    }<br><br>    \/\/ 1mm \u79d2\u9593\u306b\u8ffd\u8de1\u7269\u304c\u9032\u3080\u8ddd\u96e2 (px) \u3092\u8a2d\u5b9a<br>    setSpeed(speed) {<br>      this.#speed = speed;<br>    }<br><br>    \/\/ \u8ffd\u8de1\u7269\u3068\u3001\u6b21\u306e\u30d5\u30ec\u30fc\u30e0\u3067\u305d\u306e\u8ffd\u8de1\u7269\u304c\u79fb\u52d5\u3059\u308b\u3079\u304d\u5ea7\u6a19\u3092\u5f15\u6570\u306b\u53d6\u308b\u95a2\u6570\u3092\u767b\u9332<br>    setTracer(element, listener, x = 0, y = 0) {<br>      this.#tracers.set(element, {listener, x, y});<br>    }<br><br>    \/\/ \u8ffd\u8de1\u7269\u306e\u73fe\u5728\u4f4d\u7f6e\u3092\u8a2d\u5b9a<br>    setTracerPosition(element, x, y, relative = false) {<br>      const tracer = this.#tracers.get(element);<br>      if (tracer) {<br>        tracer.x = relative ? tracer.x + x : x;<br>        tracer.y = relative ? tracer.y + y : y;<br>      }<br>    }<br><br>    \/\/ \u767b\u9332\u3055\u308c\u3066\u3044\u308b\u8ffd\u8de1\u7269\u3092\u524a\u9664<br>    removeTracer(element) {<br>      this.#tracers.delete(element);<br>    }<br><br>    \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306e\u5ea7\u6a19\u3092\u66f4\u65b0<br>    setTarget(x, y) {<br>      this.#targetX = x;<br>      this.#targetY = y;<br>    }<br><br>    \/\/ \u53cd\u767a\u3055\u305b\u308b\u304b\u3069\u3046\u304b\u3092\u53d6\u5f97<br>    getRepulsion() {<br>      return this.#repulsion;<br>    }<br><br>    \/\/ \u53cd\u767a\u3055\u305b\u308b\u304b\u3069\u3046\u304b\u3092\u8a2d\u5b9a<br>    setRepulsion(repulsion) {<br>      this.#repulsion = repulsion;<br>    }<br><br>    \/\/ \u53cd\u767a\u3055\u305b\u308b\u8ddd\u96e2\u306e\u95be\u5024\u3092\u8a2d\u5b9a<br>    setMaxDistance(maxDistance) {<br>      this.#maxDistance = maxDistance;<br>    }<br>  };<br><br>  \/\/ Tracing \u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u4f5c\u6210 (1mm \u79d2\u9593\u306b 0.5px \u9032\u3080)<br>  const tracing = new Tracing(1.5);<br><br>  \/\/ \u30de\u30a6\u30b9\u304c\u52d5\u3044\u305f\u3068\u304d\u306b\u8ffd\u8de1\u5bfe\u8c61\u3092\u66f4\u65b0<br>  window.addEventListener('mousemove', (event) =&gt; {<br>    tracing.setTarget(event.clientX, event.clientY);<br>  });<br><br>  \/\/ \u8ffd\u5f93\u3055\u305b\u308b\u5186<br>  const circle1 = document.getElementById('circle1');<br><br>  \/\/ \u8ffd\u5f93\u3055\u305b\u308b\u5186\u3092\u767b\u9332<br>  tracing.setTracer(circle1, (x, y) =&gt; {<br>    circle1.style.left = `${x}px`;<br>    circle1.style.top = `${y}px`;<br>  });<br>&lt;\/script&gt;<br>&lt;\/body&gt;<br>&lt;\/html&gt;<\/pre>\n\n\n\n<p>\u5927\u62b5\u306e\u5834\u5408\u3001\u3053\u306e JavaScript \u90e8\u5206\u306e\u3046\u3061\u3001<\/p>\n\n\n\n<pre class=\"wp-block-preformatted modBlobCode\">  class Tracing { \/*\u7565*\/ }<br><br>  \/\/ Tracing \u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u4f5c\u6210 (1mm \u79d2\u9593\u306b 0.5px \u9032\u3080)<br>  const tracing = new Tracing(1.5);<br><br>  \/\/ \u30de\u30a6\u30b9\u304c\u52d5\u3044\u305f\u3068\u304d\u306b\u8ffd\u8de1\u5bfe\u8c61\u3092\u66f4\u65b0<br>  window.addEventListener('mousemove', (event) =&gt; {<br>    tracing.setTarget(event.clientX, event.clientY);<br>  });<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">\u307e\u3067\u306f\u30b3\u30d4\u30da\u3067\u826f\u3044\u3067\u3057\u3087\u3046\u3002<br>\u5f8c\u534a\u306e\u3001<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted modBlobCode\">  \/\/ \u8ffd\u5f93\u3055\u305b\u308b\u5186<br>  const circle1 = document.getElementById('circle1');<br><br>  \/\/ \u8ffd\u5f93\u3055\u305b\u308b\u5186\u3092\u767b\u9332<br>  tracing.setTracer(circle1, (x, y) =&gt; {<br>    circle1.style.left = `${x}px`;<br>    circle1.style.top = `${y}px`;<br>  });<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">\u90e8\u5206\u3067\u306f\u3001\u8ffd\u5f93\u3055\u305b\u305f\u3044\u8981\u7d20\u3092\u53d6\u5f97\u3057\u3066 `setTracer` \u30e1\u30bd\u30c3\u30c9\u3067\u767b\u9332\u3057\u3066\u3044\u307e\u3059\u3002 `const circle1 = ` \u306e\u90e8\u5206\u3092\u8ffd\u5f93\u3055\u305b\u305f\u3044\u8981\u7d20\u306b\u66f8\u304d\u63db\u3048\u3001 `tracing.setTracer` \u306e\u90e8\u5206\u3067\u8ffd\u5f93\u3055\u305b\u305f\u3044\u8981\u7d20\u306e\u30b9\u30bf\u30a4\u30eb\u3092\u66f4\u65b0\u3059\u308b\u3088\u3046\u306b\u66f8\u304d\u63db\u3048\u3066\u304f\u3060\u3055\u3044\u3002<br><br>\u3082\u3057\u3001\u8ffd\u5f93\u3059\u308b\u30b9\u30d4\u30fc\u30c9\u3092\u5909\u3048\u305f\u3044\u5834\u5408\u306f\u3001 `new Tracing(1.5);` \u306e `1.5` \u306e\u90e8\u5206\u3092\u66f8\u304d\u63db\u3048\u3066\u304f\u3060\u3055\u3044\u3002\u6570\u5024\u304c\u5927\u304d\u3044\u307b\u3069\u901f\u304f\u8ffd\u5f93\u3057\u307e\u3059\u3002<\/pre>\n\n\n\n<h4 class=\"wp-block-comcent-blog-headline01 modHeadline\">\u3082\u3046\u5c11\u3057\u51dd\u3063\u305f\u7528\u4f8b<\/h4>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1584\" height=\"1012\" loading=\"lazy\" src=\"https:\/\/comcent.co.jp\/blog\/wp-content\/uploads\/2025\/10\/noji-2025-10-01-blog-0003.gif\" alt=\"\" class=\"wp-image-5886\"\/><\/figure>\n\n\n\n<pre class=\"wp-block-preformatted modBlobCode\">&lt;!doctype html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>    &lt;meta charset=\"utf-8\"&gt;<br>    &lt;title&gt;\u30ab\u30fc\u30bd\u30eb\u8ffd\u8de1 DEMO&lt;\/title&gt;<br>    &lt;style&gt;<br>      .circle {<br>        transition: top, left 0.1s linear;<br>        position: absolute;<br>        width: 50px;<br>        height: 50px;<br>        border-radius: 50%;<br>        pointer-events: none; \/* \u30af\u30ea\u30c3\u30af\u3092\u7121\u8996 *\/<br>        transform: translate(-50%, -50%); \/* \u4e2d\u5fc3\u306b\u914d\u7f6e *\/<br>      }<br>    &lt;\/style&gt;<br>&lt;\/head&gt;<br>&lt;body&gt;<br>    &lt;h1&gt;\u30ab\u30fc\u30bd\u30eb\u8ffd\u8de1 DEMO&lt;\/h1&gt;<br>    &lt;div id=\"circle1\" class=\"circle\" style=\"background-color: red;\"&gt;&lt;\/div&gt;<br>    &lt;div id=\"circle2\" class=\"circle\" style=\"background-color: blue;\"&gt;&lt;\/div&gt;<br>    &lt;div id=\"circle3\" class=\"circle\" style=\"background-color: green;\"&gt;&lt;\/div&gt;<br>    &lt;div id=\"circle4\" class=\"circle\" style=\"background-color: yellow;\"&gt;&lt;\/div&gt;<br>    &lt;div id=\"circle5\" class=\"circle\" style=\"background-color: purple;\"&gt;&lt;\/div&gt;<br>    &lt;script defer&gt;<br>      class Tracing {<br>        \/\/ 1mm \u79d2\u9593\u306b\u8ffd\u8de1\u7269\u304c\u9032\u3080\u8ddd\u96e2 (px)<br>        #speed;<br><br>        \/\/ \u524d\u56de\u306e\u30eb\u30fc\u30d7\u5b9f\u884c\u6642\u9593<br>        #beforeTimestamp = 0;<br><br>        \/\/ \u8ffd\u8de1\u7269\u3068\u305d\u306e\u5ea7\u6a19\u914d\u5217<br>        #tracers = new Map();<br><br>        \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306e\u73fe X \u5ea7\u6a19<br>        #targetX = 0;<br><br>        \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306e\u73fe Y \u5ea7\u6a19<br>        #targetY = 0;<br><br>        \/\/ \u53cd\u767a\u3055\u305b\u308b\u304b\u3069\u3046\u304b<br>        #repulsion = false;<br><br>        \/\/ \u53cd\u767a\u3055\u305b\u308b\u8ddd\u96e2\u306e\u95be\u5024<br>        #maxDistance = 9999;<br><br>        \/\/ \u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u30eb\u30fc\u30d7\u306e\u958b\u59cb<br>        constructor(speed) {<br>          const loop = () =&gt; {<br>            \/\/ \u73fe\u5728\u306e\u6642\u9593\u3092\u53d6\u5f97<br>            const timestamp = Date.now();<br><br>            \/\/ \u524d\u56de\u306e\u30eb\u30fc\u30d7\u304b\u3089\u306e\u7d4c\u904e\u6642\u9593\u3092\u8a08\u7b97<br>            const delta = timestamp - this.#beforeTimestamp;<br><br>            \/\/ \u4eca\u56de\u306e\u30eb\u30fc\u30d7\u6642\u9593\u3092\u4fdd\u5b58<br>            this.#beforeTimestamp = timestamp;<br><br>            \/\/ \u8ffd\u8de1\u7269\u3092\u3059\u3079\u3066\u66f4\u65b0<br>            this.#tracers.forEach((parameter) =&gt; {<br>              \/\/ \u8ffd\u8de1\u7269\u306e\u73fe\u5728\u4f4d\u7f6e\u3092\u53d6\u5f97<br>              const {x: tracerX, y: tracerY, listener} = parameter;<br><br>              \/\/ \u8ffd\u8de1\u7269\u3068\u8ffd\u8de1\u5bfe\u8c61\u306e\u8ddd\u96e2\u3092\u8a08\u7b97<br>              const dx = this.#targetX - tracerX;<br>              const dy = this.#targetY - tracerY;<br>              const distance = Math.sqrt(dx * dx + dy * dy);<br><br>              \/\/ 1 \u30d5\u30ec\u30fc\u30e0\u3067\u9032\u3080\u8ddd\u96e2\u3092\u8a08\u7b97<br>              const distancePerFrame = this.#speed * delta;<br><br>              \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306b\u5411\u304b\u3046\u5358\u4f4d\u30d9\u30af\u30c8\u30eb\u3092\u8a08\u7b97<br>              if (distance &gt; distancePerFrame) {<br>                \/\/ \u53cd\u767a\u3055\u305b\u308b\u5834\u5408\u3001\u95be\u5024\u4ee5\u5185\u306a\u3089\u8ffd\u8de1\u5bfe\u8c61\u304b\u3089\u9060\u3056\u3051\u308b<br>                const newX = tracerX + dx \/ distance * distancePerFrame * (this.#repulsion &amp;&amp; distance &lt; this.#maxDistance ? -1 : 1);<br>                const newY = tracerY + dy \/ distance * distancePerFrame * (this.#repulsion &amp;&amp; distance &lt; this.#maxDistance ? -1 : 1);<br><br>                \/\/ \u8ffd\u8de1\u7269\u306e\u4f4d\u7f6e\u3092\u66f4\u65b0<br>                parameter.x = newX;<br>                parameter.y = newY;<br><br>                \/\/ \u30ea\u30b9\u30ca\u30fc\u95a2\u6570\u3092\u547c\u3073\u51fa\u3057<br>                listener(newX, newY);<br>              }<br><br>              \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306b\u76f4\u63a5\u79fb\u52d5<br>              else {<br>                parameter.x = this.#targetX;<br>                parameter.y = this.#targetY;<br><br>                \/\/ \u30ea\u30b9\u30ca\u30fc\u95a2\u6570\u3092\u547c\u3073\u51fa\u3057<br>                listener(this.#targetX, this.#targetY);<br>              }<br>            });<br><br>            \/\/ \u6b21\u306e\u30eb\u30fc\u30d7\u3092\u4e88\u7d04<br>            requestAnimationFrame(loop);<br>          };<br><br>          \/\/ \u30b9\u30d4\u30fc\u30c9\u306e\u8a2d\u5b9a<br>          this.#speed = speed;<br><br>          \/\/ \u30eb\u30fc\u30d7 1 \u56de\u76ee\u304c\u53c2\u7167\u3059\u308b\u6642\u9593\u3092\u8a2d\u5b9a<br>          this.#beforeTimestamp = Date.now();<br><br>          \/\/ requestAnimationFrame \u3067\u30eb\u30fc\u30d7\u3092\u958b\u59cb<br>          requestAnimationFrame(loop);<br>        }<br><br>        \/\/ 1mm \u79d2\u9593\u306b\u8ffd\u8de1\u7269\u304c\u9032\u3080\u8ddd\u96e2 (px) \u3092\u8a2d\u5b9a<br>        setSpeed(speed) {<br>          this.#speed = speed;<br>        }<br><br>        \/\/ \u8ffd\u8de1\u7269\u3068\u3001\u6b21\u306e\u30d5\u30ec\u30fc\u30e0\u3067\u305d\u306e\u8ffd\u8de1\u7269\u304c\u79fb\u52d5\u3059\u308b\u3079\u304d\u5ea7\u6a19\u3092\u5f15\u6570\u306b\u53d6\u308b\u95a2\u6570\u3092\u767b\u9332<br>        setTracer(element, listener, x = 0, y = 0) {<br>          this.#tracers.set(element, {listener, x, y});<br>        }<br><br>        \/\/ \u8ffd\u8de1\u7269\u306e\u73fe\u5728\u4f4d\u7f6e\u3092\u8a2d\u5b9a<br>        setTracerPosition(element, x, y, relative = false) {<br>          const tracer = this.#tracers.get(element);<br>          if (tracer) {<br>            tracer.x = relative ? tracer.x + x : x;<br>            tracer.y = relative ? tracer.y + y : y;<br>          }<br>        }<br><br>        \/\/ \u767b\u9332\u3055\u308c\u3066\u3044\u308b\u8ffd\u8de1\u7269\u3092\u524a\u9664<br>        removeTracer(element) {<br>          this.#tracers.delete(element);<br>        }<br><br>        \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306e\u5ea7\u6a19\u3092\u66f4\u65b0<br>        setTarget(x, y) {<br>          this.#targetX = x;<br>          this.#targetY = y;<br>        }<br><br>        \/\/ \u53cd\u767a\u3055\u305b\u308b\u304b\u3069\u3046\u304b\u3092\u53d6\u5f97<br>        getRepulsion() {<br>          return this.#repulsion;<br>        }<br><br>        \/\/ \u53cd\u767a\u3055\u305b\u308b\u304b\u3069\u3046\u304b\u3092\u8a2d\u5b9a<br>        setRepulsion(repulsion) {<br>          this.#repulsion = repulsion;<br>        }<br><br>        \/\/ \u53cd\u767a\u3055\u305b\u308b\u8ddd\u96e2\u306e\u95be\u5024\u3092\u8a2d\u5b9a<br>        setMaxDistance(maxDistance) {<br>          this.#maxDistance = maxDistance;<br>        }<br>      };<br><br>      \/\/ Tracing \u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u4f5c\u6210 (1mm \u79d2\u9593\u306b 0.5px \u9032\u3080)<br>      const tracing = new Tracing(1.5);<br><br>      \/\/ \u30de\u30a6\u30b9\u304c\u52d5\u3044\u305f\u3068\u304d\u306b\u8ffd\u8de1\u5bfe\u8c61\u3092\u66f4\u65b0<br>      window.addEventListener('mousemove', (event) =&gt; {<br>        tracing.setTarget(event.clientX, event.clientY);<br>      });<br><br>      \/\/ \u8ffd\u5f93\u3055\u305b\u308b\u5186<br>      const circle1 = document.getElementById('circle1');<br>      const circle2 = document.getElementById('circle2');<br>      const circle3 = document.getElementById('circle3');<br>      const circle4 = document.getElementById('circle4');<br>      const circle5 = document.getElementById('circle5');<br><br>      \/\/ \u8ffd\u5f93\u3055\u305b\u308b\u5186\u3092\u767b\u9332<br>      tracing.setTracer(circle1, (x, y) =&gt; {<br>        circle1.style.left = `${x}px`;<br>        circle1.style.top = `${y}px`;<br>      }, 0, 0);<br>      tracing.setTracer(circle2, (x, y) =&gt; {<br>        circle2.style.left = `${x}px`;<br>        circle2.style.top = `${y}px`;<br>      }, 200, 0);<br>      tracing.setTracer(circle3, (x, y) =&gt; {<br>        circle3.style.left = `${x}px`;<br>        circle3.style.top = `${y}px`;<br>      }, 0, 200);<br>      tracing.setTracer(circle4, (x, y) =&gt; {<br>        circle4.style.left = `${x}px`;<br>        circle4.style.top = `${y}px`;<br>      }, 200, 200);<br>      tracing.setTracer(circle5, (x, y) =&gt; {<br>        circle5.style.left = `${x}px`;<br>        circle5.style.top = `${y}px`;<br>      }, 100, 100);<br><br>      \/\/ \u30af\u30ea\u30c3\u30af\u3067\u53cd\u767a\u30e2\u30fc\u30c9\u3092\u5207\u308a\u66ff\u3048<br>      window.addEventListener('click', () =&gt; {<br>        tracing.setTracerPosition(circle1, Math.random() * 100 - 50, Math.random() * 100 - 50, true);<br>        tracing.setTracerPosition(circle2, Math.random() * 100 - 50, Math.random() * 100 - 50, true);<br>        tracing.setTracerPosition(circle3, Math.random() * 100 - 50, Math.random() * 100 - 50, true);<br>        tracing.setTracerPosition(circle4, Math.random() * 100 - 50, Math.random() * 100 - 50, true);<br>        tracing.setTracerPosition(circle5, Math.random() * 100 - 50, Math.random() * 100 - 50, true);<br><br>        tracing.setRepulsion(!tracing.getRepulsion());<br>      });<br>    &lt;\/script&gt;<br>&lt;\/body&gt;<br>&lt;\/html&gt;<\/pre>\n\n\n\n<p>\u3053\u306e\u4f8b\u3067\u306f\u30015 \u3064\u306e\u5186\u304c\u30ab\u30fc\u30bd\u30eb\u3092\u8ffd\u3044\u304b\u3051\u307e\u3059\u3002\u3055\u3089\u306b\u3001\u30af\u30ea\u30c3\u30af\u3059\u308b\u305f\u3073\u306b\u5186\u304c\u30e9\u30f3\u30c0\u30e0\u306b\u5c11\u3057\u52d5\u304d\u3001\u53cd\u767a\u30e2\u30fc\u30c9\u304c\u5207\u308a\u66ff\u308f\u308a\u307e\u3059\u3002\u53cd\u767a\u30e2\u30fc\u30c9\u3067\u306f\u3001\u5186\u304c\u30ab\u30fc\u30bd\u30eb\u304b\u3089\u9060\u3056\u304b\u308b\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u3002\u30ab\u30fc\u30bd\u30eb\u304b\u3089\u9060\u3056\u304b\u308b\u9650\u754c\u5024\u306f <code>setMaxDistance<\/code> \u30e1\u30bd\u30c3\u30c9\u3067\u8a2d\u5b9a\u3067\u304d\u307e\u3059 (\u30c7\u30d5\u30a9\u30eb\u30c8\u306f 9999px)\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-comcent-blog-headline01 modHeadline\">\u3055\u3089\u306b\u51dd\u3063\u3066 Canvas \u3067\u63cf\u753b\u3059\u308b\u7528\u4f8b<\/h4>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1584\" height=\"1012\" loading=\"lazy\" src=\"https:\/\/comcent.co.jp\/blog\/wp-content\/uploads\/2025\/10\/noji-2025-10-01-blog-0004.gif\" alt=\"\" class=\"wp-image-5887\"\/><\/figure>\n\n\n\n<pre class=\"wp-block-preformatted modBlobCode\">&lt;!doctype html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>  &lt;meta charset=\"utf-8\"&gt;<br>  &lt;title&gt;\u30ab\u30fc\u30bd\u30eb\u8ffd\u8de1 DEMO (Canvas)&lt;\/title&gt;<br>  &lt;style&gt;<br>      body {<br>          margin: 0;<br>          overflow: hidden;<br>      }<br>      canvas {<br>          display: block;<br>          background: #f0f0f0;<br>      }<br>  &lt;\/style&gt;<br>&lt;\/head&gt;<br>&lt;body&gt;<br>&lt;canvas id=\"canvas\"&gt;&lt;\/canvas&gt;<br>&lt;script&gt;<br>  class Tracing {<br>    \/\/ 1mm \u79d2\u9593\u306b\u8ffd\u8de1\u7269\u304c\u9032\u3080\u8ddd\u96e2 (px)<br>    #speed;<br><br>    \/\/ \u524d\u56de\u306e\u30eb\u30fc\u30d7\u5b9f\u884c\u6642\u9593<br>    #beforeTimestamp = 0;<br><br>    \/\/ \u8ffd\u8de1\u7269\u3068\u305d\u306e\u5ea7\u6a19\u914d\u5217<br>    #tracers = new Map();<br><br>    \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306e\u73fe X \u5ea7\u6a19<br>    #targetX = 0;<br><br>    \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306e\u73fe Y \u5ea7\u6a19<br>    #targetY = 0;<br><br>    \/\/ \u53cd\u767a\u3055\u305b\u308b\u304b\u3069\u3046\u304b<br>    #repulsion = false;<br><br>    \/\/ \u53cd\u767a\u3055\u305b\u308b\u8ddd\u96e2\u306e\u95be\u5024<br>    #maxDistance = 9999;<br><br>    \/\/ \u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u30eb\u30fc\u30d7\u306e\u958b\u59cb<br>    constructor(speed) {<br>      const loop = () =&gt; {<br>        \/\/ \u73fe\u5728\u306e\u6642\u9593\u3092\u53d6\u5f97<br>        const timestamp = Date.now();<br><br>        \/\/ \u524d\u56de\u306e\u30eb\u30fc\u30d7\u304b\u3089\u306e\u7d4c\u904e\u6642\u9593\u3092\u8a08\u7b97<br>        const delta = timestamp - this.#beforeTimestamp;<br><br>        \/\/ \u4eca\u56de\u306e\u30eb\u30fc\u30d7\u6642\u9593\u3092\u4fdd\u5b58<br>        this.#beforeTimestamp = timestamp;<br><br>        \/\/ \u8ffd\u8de1\u7269\u3092\u3059\u3079\u3066\u66f4\u65b0<br>        this.#tracers.forEach((parameter) =&gt; {<br>          \/\/ \u8ffd\u8de1\u7269\u306e\u73fe\u5728\u4f4d\u7f6e\u3092\u53d6\u5f97<br>          const {x: tracerX, y: tracerY, listener} = parameter;<br><br>          \/\/ \u8ffd\u8de1\u7269\u3068\u8ffd\u8de1\u5bfe\u8c61\u306e\u8ddd\u96e2\u3092\u8a08\u7b97<br>          const dx = this.#targetX - tracerX;<br>          const dy = this.#targetY - tracerY;<br>          const distance = Math.sqrt(dx * dx + dy * dy);<br><br>          \/\/ 1 \u30d5\u30ec\u30fc\u30e0\u3067\u9032\u3080\u8ddd\u96e2\u3092\u8a08\u7b97<br>          const distancePerFrame = this.#speed * delta;<br><br>          \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306b\u5411\u304b\u3046\u5358\u4f4d\u30d9\u30af\u30c8\u30eb\u3092\u8a08\u7b97<br>          if (distance &gt; distancePerFrame) {<br>            \/\/ \u53cd\u767a\u3055\u305b\u308b\u5834\u5408\u3001\u95be\u5024\u4ee5\u5185\u306a\u3089\u8ffd\u8de1\u5bfe\u8c61\u304b\u3089\u9060\u3056\u3051\u308b<br>            const newX = tracerX + dx \/ distance * distancePerFrame * (this.#repulsion &amp;&amp; distance &lt; this.#maxDistance ? -1 : 1);<br>            const newY = tracerY + dy \/ distance * distancePerFrame * (this.#repulsion &amp;&amp; distance &lt; this.#maxDistance ? -1 : 1);<br><br>            \/\/ \u8ffd\u8de1\u7269\u306e\u4f4d\u7f6e\u3092\u66f4\u65b0<br>            parameter.x = newX;<br>            parameter.y = newY;<br><br>            \/\/ \u30ea\u30b9\u30ca\u30fc\u95a2\u6570\u3092\u547c\u3073\u51fa\u3057<br>            listener(newX, newY);<br>          }<br><br>          \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306b\u76f4\u63a5\u79fb\u52d5<br>          else {<br>            parameter.x = this.#targetX;<br>            parameter.y = this.#targetY;<br><br>            \/\/ \u30ea\u30b9\u30ca\u30fc\u95a2\u6570\u3092\u547c\u3073\u51fa\u3057<br>            listener(this.#targetX, this.#targetY);<br>          }<br>        });<br><br>        \/\/ \u6b21\u306e\u30eb\u30fc\u30d7\u3092\u4e88\u7d04<br>        requestAnimationFrame(loop);<br>      };<br><br>      \/\/ \u30b9\u30d4\u30fc\u30c9\u306e\u8a2d\u5b9a<br>      this.#speed = speed;<br><br>      \/\/ \u30eb\u30fc\u30d7 1 \u56de\u76ee\u304c\u53c2\u7167\u3059\u308b\u6642\u9593\u3092\u8a2d\u5b9a<br>      this.#beforeTimestamp = Date.now();<br><br>      \/\/ requestAnimationFrame \u3067\u30eb\u30fc\u30d7\u3092\u958b\u59cb<br>      requestAnimationFrame(loop);<br>    }<br><br>    \/\/ 1mm \u79d2\u9593\u306b\u8ffd\u8de1\u7269\u304c\u9032\u3080\u8ddd\u96e2 (px) \u3092\u8a2d\u5b9a<br>    setSpeed(speed) {<br>      this.#speed = speed;<br>    }<br><br>    \/\/ \u8ffd\u8de1\u7269\u3068\u3001\u6b21\u306e\u30d5\u30ec\u30fc\u30e0\u3067\u305d\u306e\u8ffd\u8de1\u7269\u304c\u79fb\u52d5\u3059\u308b\u3079\u304d\u5ea7\u6a19\u3092\u5f15\u6570\u306b\u53d6\u308b\u95a2\u6570\u3092\u767b\u9332<br>    setTracer(element, listener, x = 0, y = 0) {<br>      this.#tracers.set(element, {listener, x, y});<br>    }<br><br>    \/\/ \u8ffd\u8de1\u7269\u306e\u73fe\u5728\u4f4d\u7f6e\u3092\u8a2d\u5b9a<br>    setTracerPosition(element, x, y, relative = false) {<br>      const tracer = this.#tracers.get(element);<br>      if (tracer) {<br>        tracer.x = relative ? tracer.x + x : x;<br>        tracer.y = relative ? tracer.y + y : y;<br>      }<br>    }<br><br>    \/\/ \u767b\u9332\u3055\u308c\u3066\u3044\u308b\u8ffd\u8de1\u7269\u3092\u524a\u9664<br>    removeTracer(element) {<br>      this.#tracers.delete(element);<br>    }<br><br>    \/\/ \u8ffd\u8de1\u5bfe\u8c61\u306e\u5ea7\u6a19\u3092\u66f4\u65b0<br>    setTarget(x, y) {<br>      this.#targetX = x;<br>      this.#targetY = y;<br>    }<br><br>    \/\/ \u53cd\u767a\u3055\u305b\u308b\u304b\u3069\u3046\u304b\u3092\u53d6\u5f97<br>    getRepulsion() {<br>      return this.#repulsion;<br>    }<br><br>    \/\/ \u53cd\u767a\u3055\u305b\u308b\u304b\u3069\u3046\u304b\u3092\u8a2d\u5b9a<br>    setRepulsion(repulsion) {<br>      this.#repulsion = repulsion;<br>    }<br><br>    \/\/ \u53cd\u767a\u3055\u305b\u308b\u8ddd\u96e2\u306e\u95be\u5024\u3092\u8a2d\u5b9a<br>    setMaxDistance(maxDistance) {<br>      this.#maxDistance = maxDistance;<br>    }<br>  };<br><br>  \/\/ --- Canvas \u521d\u671f\u5316 ---<br>  const canvas = document.getElementById('canvas');<br>  const ctx = canvas.getContext('2d');<br><br>  \/\/ Tracing \u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u4f5c\u6210 (1mm \u79d2\u9593\u306b 0.5px \u9032\u3080)<br>  const tracing = new Tracing(1.5);<br><br>  \/\/ \u5186\u306e\u30c7\u30fc\u30bf<br>  const circles = [<br>    {color: 'red', radius: 25},<br>    {color: 'blue', radius: 25},<br>    {color: 'green', radius: 25},<br>    {color: 'yellow', radius: 25},<br>    {color: 'purple', radius: 25},<br>  ];<br><br>  \/\/ \u63cf\u753b\u7528\u306e\u4f4d\u7f6e\u60c5\u5831<br>  circles.forEach((circle, i) =&gt; {<br>    tracing.setTracer(circle, (x, y) =&gt; {<br>      circle.x = x;<br>      circle.y = y;<br>    }, 100 + i * 80, 100 + i * 60);<br>  });<br><br>  \/\/ \u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u63cf\u753b<br>  function render() {<br>    \/\/ \u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u306e\u305f\u3081\u306b\u30ad\u30e3\u30f3\u30d0\u30b9\u3092\u30af\u30ea\u30a2<br>    ctx.clearRect(0, 0, canvas.width, canvas.height);<br><br>    \/\/ \u7d4c\u904e\u79d2\u3092\u30e9\u30b8\u30a2\u30f3\u306b\u5909\u63db<br>    const rad = Date.now() \/ 150;<br><br>    \/\/ \u30ab\u30f3\u30d0\u30b9\u4e2d\u592e\u304b\u3089\u534a\u5f84 300 \u306e\u5186\u3092\u63cf\u304f\u3088\u3046\u306b\u30bf\u30fc\u30b2\u30c3\u30c8\u3092\u79fb\u52d5<br>    const centerX = canvas.width \/ 2;<br>    const centerY = canvas.height \/ 2;<br>    const targetX = centerX + Math.cos(rad) * 300;<br>    const targetY = centerY + Math.sin(rad) * 300;<br>    tracing.setTarget(targetX, targetY);<br><br>    \/\/ \u8ffd\u8de1\u5bfe\u8c61\u3092\u63cf\u753b<br>    ctx.beginPath();<br>    ctx.arc(targetX, targetY, 10, 0, Math.PI * 2);<br>    ctx.fillStyle = 'black';<br>    ctx.fill();<br><br>    \/\/ \u5404\u5186\u3092\u63cf\u753b<br>    circles.forEach((c) =&gt; {<br>      ctx.beginPath();<br>      ctx.arc(c.x, c.y, c.radius, 0, Math.PI * 2);<br>      ctx.fillStyle = c.color;<br>      ctx.fill();<br>    });<br><br>    \/\/ \u6b21\u306e\u30d5\u30ec\u30fc\u30e0\u3092\u4e88\u7d04<br>    requestAnimationFrame(render);<br>  }<br>  render();<br><br>  \/\/ \u30af\u30ea\u30c3\u30af\u3067\u53cd\u767a\u30e2\u30fc\u30c9\u5207\u66ff<br>  window.addEventListener('click', () =&gt; {<br>    circles.forEach(c =&gt; {<br>      tracing.setTracerPosition(c, Math.random() * 500 - 250, Math.random() * 500 - 250, true);<br>    });<br>    tracing.setRepulsion(!tracing.getRepulsion());<br>  });<br><br>  \/\/ \u30a6\u30a3\u30f3\u30c9\u30a6\u30ea\u30b5\u30a4\u30ba\u5bfe\u5fdc<br>  function resize() {<br>    canvas.width = window.innerWidth;<br>    canvas.height = window.innerHeight;<br>  }<br>  window.addEventListener(\"resize\", resize);<br>  resize();<br>&lt;\/script&gt;<br>&lt;\/body&gt;<br>&lt;\/html&gt;<\/pre>\n\n\n\n<p>\u3053\u306e\u4f8b\u3067\u306f\u3001Canvas \u3092\u4f7f\u3063\u3066\u63cf\u753b\u3057\u3066\u3044\u307e\u3059\u3002\u3055\u3089\u306b\u3001\u8ffd\u3044\u304b\u3051\u308b\u5bfe\u8c61\u304c\u30de\u30a6\u30b9\u30ab\u30fc\u30bd\u30eb\u3067\u306f\u306a\u304f\u3001\u753b\u9762\u4e2d\u592e\u3092\u4e2d\u5fc3\u306b\u5186\u3092\u63cf\u304f\u9ed2\u3044\u70b9\u3068\u306a\u3063\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<p><code>Tracing<\/code> \u30af\u30e9\u30b9\u306f DOM \u306b\u4f9d\u5b58\u3057\u3066\u3044\u306a\u3044\u306e\u3067\u3001 Canvas \u306e\u63cf\u753b\u306b\u3082\u305d\u306e\u307e\u307e\u4f7f\u3048\u307e\u3059\u3002 <code>setTracer<\/code> \u30e1\u30bd\u30c3\u30c9\u306e\u7b2c\u4e00\u5f15\u6570\u306b\u4efb\u610f\u306e\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u6e21\u3057\u3001\u7b2c\u4e8c\u5f15\u6570\u306e\u30ea\u30b9\u30ca\u30fc\u95a2\u6570\u3067\u305d\u306e\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u4f4d\u7f6e\u60c5\u5831\u3092\u66f4\u65b0\u3059\u308b\u3088\u3046\u306b\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-comcent-blog-headline01 modHeadline\">\u307e\u3068\u3081<\/h4>\n\n\n\n<p>\u305d\u3093\u306a\u306b\u96e3\u3057\u3044\u30b3\u30fc\u30c9\u3067\u306f\u306a\u3044\u306e\u3067\u3001\u5fc5\u8981\u306b\u5fdc\u3058\u3066\u6539\u9020\u3059\u308b\u3053\u3068\u3082\u3067\u304d\u308b\u3068\u601d\u3044\u307e\u3059\u3002\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u306f\u602a\u3057\u3044\u3067\u3059\u304c\u3001\u30b2\u30fc\u30e0\u306a\u3069\u306b\u3082\u8ee2\u7528\u3067\u304d\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u3072\u3068\u6614\u306f\u30ab\u30fc\u30bd\u30eb\u3092\u30b5\u30a4\u30c8\u72ec\u81ea\u306e\u30a2\u30a4\u30b3\u30f3\u306b\u5909\u3048\u305f\u308a\u3057\u305f\u30b5\u30a4\u30c8\u3092\u898b\u304b\u3051\u305f\u3082\u306e\u3067\u3059\u304c\u3001\u3055\u3089\u306b\u51dd\u3063\u305f\u3082\u306e\u3068\u3057\u3066\u30ab\u30fc\u30bd\u30eb\u3092\u30a2\u30a4\u30b3\u30f3\u306a\u3069\u304c\u8ffd\u3044\u304b\u3051\u3066\u304f\u308b\u3088\u3046\u306a\u6f14\u51fa\u3082\u898b\u304b\u3051\u305f\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002\u3061\u3087\u3046\u3069\u30b2\u30fc\u30e0\u3067\u4e3b\u4eba\u516c\u3092\u8ffd\u3044\u304b\u3051\u3066\u304f\u308b\u8ffd\u5c3e\u5f3e [&hellip;]<\/p>\n","protected":false},"author":7,"featured_media":5884,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13],"tags":[101,76,90,92],"coauthors":[45],"class_list":["post-5883","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-frontend","tag-html","tag-javascript","tag-js","tag-92"],"author_meta":{"nickname":"N.Go","position":"\u30d7\u30ed\u30b0\u30e9\u30de\u30fc","icon_url":"https:\/\/comcent.co.jp\/blog\/images\/authorNg-150x150.png","author_url":"https:\/\/comcent.co.jp\/blog\/archives\/author\/noji\/"},"tag_names":["HTML","JavaScript","JS","\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9"],"thumbnail_url":"https:\/\/comcent.co.jp\/blog\/wp-content\/uploads\/2025\/10\/noji-2025-10-01-blog-0001-300x200.png","_links":{"self":[{"href":"https:\/\/comcent.co.jp\/blog\/wp-json\/wp\/v2\/posts\/5883","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/comcent.co.jp\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/comcent.co.jp\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/comcent.co.jp\/blog\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/comcent.co.jp\/blog\/wp-json\/wp\/v2\/comments?post=5883"}],"version-history":[{"count":4,"href":"https:\/\/comcent.co.jp\/blog\/wp-json\/wp\/v2\/posts\/5883\/revisions"}],"predecessor-version":[{"id":6075,"href":"https:\/\/comcent.co.jp\/blog\/wp-json\/wp\/v2\/posts\/5883\/revisions\/6075"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/comcent.co.jp\/blog\/wp-json\/wp\/v2\/media\/5884"}],"wp:attachment":[{"href":"https:\/\/comcent.co.jp\/blog\/wp-json\/wp\/v2\/media?parent=5883"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/comcent.co.jp\/blog\/wp-json\/wp\/v2\/categories?post=5883"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/comcent.co.jp\/blog\/wp-json\/wp\/v2\/tags?post=5883"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/comcent.co.jp\/blog\/wp-json\/wp\/v2\/coauthors?post=5883"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}