{"id":66,"date":"2025-12-01T19:43:49","date_gmt":"2025-12-01T19:43:49","guid":{"rendered":"https:\/\/jettdax.com\/?page_id=66"},"modified":"2025-12-01T20:56:54","modified_gmt":"2025-12-01T20:56:54","slug":"102-2","status":"publish","type":"page","link":"https:\/\/jettdax.com\/index.php\/102-2\/","title":{"rendered":"102"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"nl\">\n<head>\n  <meta charset=\"UTF-8\" \/>\n  <title>Studio Luw \u2013 Streep met Swoosh Staart<\/title>\n  <style>\n    html, body {\n      margin: 0;\n      padding: 0;\n      height: 100%;\n      overflow: hidden;\n      background: radial-gradient(circle at top, #020617 0%, #000 70%);\n      font-family: system-ui, -apple-system, BlinkMacSystemFont, \"SF Pro Text\", Inter, sans-serif;\n    }\n\n    canvas {\n      display: block;\n    }\n\n    .overlay {\n      position: fixed;\n      inset: 0;\n      pointer-events: none;\n      display: flex;\n      justify-content: space-between;\n      align-items: flex-end;\n      padding: 24px 28px;\n      box-sizing: border-box;\n      color: #e5e7eb;\n      font-size: 11px;\n      text-transform: uppercase;\n      letter-spacing: 0.16em;\n      opacity: 0.8;\n    }\n\n    .badge {\n      display: inline-flex;\n      align-items: center;\n      gap: 8px;\n    }\n\n    .dot {\n      width: 7px;\n      height: 7px;\n      border-radius: 999px;\n      background: radial-gradient(circle, #4ade80, #22c55e);\n      box-shadow: 0 0 14px rgba(34, 197, 94, 0.9);\n    }\n\n    @media (max-width: 768px) {\n      .overlay {\n        padding: 14px 12px;\n        flex-direction: column;\n        align-items: flex-start;\n        gap: 8px;\n        font-size: 9px;\n      }\n    }\n  <\/style>\n<\/head>\n<body>\n  <canvas id=\"cloth\"><\/canvas>\n\n  <div class=\"overlay\">\n    <div class=\"badge\">\n      <div class=\"dot\"><\/div>\n      <span>STUDIO LUW STREEP \u2022 SWOOSH STAART<\/span>\n    <\/div>\n    <div>WIND \u2192 NADEINEN \u2192 EINDIGT IN OMGEKEERDE NIKE-KROMMING<\/div>\n  <\/div>\n\n  <script>\n    const canvas = document.getElementById('cloth');\n    const ctx = canvas.getContext('2d');\n\n    let width = window.innerWidth;\n    let height = window.innerHeight;\n    canvas.width = width;\n    canvas.height = height;\n\n    \/\/ --------- Simulatie parameters ----------\n    const NUM_SEGMENTS = 30;\n    const SEGMENT_LENGTH = 18;\n\n    const GRAVITY = 820;\n    const DAMPING = 0.993;           \/\/ weinig demping \u2192 mooi nadeinen\n    const CONSTRAINT_ITER = 7;\n\n    const FIXED_COUNT = 18;          \/\/ vast, horizontaal deel\n\n    \/\/ Wind-profiel\n    const RAMP_UP_TIME   = 2.5;      \/\/ opbouwen\n    const STRONG_TIME    = 6.0;      \/\/ volle wind\n    const FADE_TIME      = 16.0;     \/\/ langzaam uitfaden\n    const START_TIME     = performance.now() \/ 1000;\n\n    const anchorX = width * 0.2;\n    const anchorY = height * 0.3;\n\n    class Point {\n      constructor(x, y, pinned = false) {\n        this.x = x;\n        this.y = y;\n        this.oldx = x;\n        this.oldy = y;\n        this.pinned = pinned;\n      }\n\n      applyVerlet(dt, windForceX, gravity) {\n        if (this.pinned) return;\n\n        const vx = (this.x - this.oldx) * DAMPING;\n        const vy = (this.y - this.oldy) * DAMPING;\n\n        this.oldx = this.x;\n        this.oldy = this.y;\n\n        const ax = windForceX;\n        const ay = gravity;\n\n        this.x += vx + ax * dt * dt;\n        this.y += vy + ay * dt * dt;\n      }\n    }\n\n    const points = [];\n    for (let i = 0; i < NUM_SEGMENTS; i++) {\n      const x = anchorX + i * SEGMENT_LENGTH;\n      const y = anchorY;\n      const pinned = i === 0;\n      points.push(new Point(x, y, pinned));\n    }\n\n    let lastTime = performance.now() \/ 1000;\n\n    function getWindFactor(t) {\n      const phase1End = RAMP_UP_TIME;\n      const phase2End = RAMP_UP_TIME + STRONG_TIME;\n      const phase3End = RAMP_UP_TIME + STRONG_TIME + FADE_TIME;\n\n      if (t < 0) return 0;\n\n      \/\/ Opbouw\n      if (t < phase1End) {\n        const k = t \/ RAMP_UP_TIME;      \/\/ 0 \u2192 1\n        return k * k;                    \/\/ ease-in\n      }\n\n      \/\/ Sterke wind\n      if (t < phase2End) {\n        return 1.0;\n      }\n\n      \/\/ Uitfaden\n      if (t < phase3End) {\n        const u = (t - phase2End) \/ FADE_TIME; \/\/ 0 \u2192 1\n        const f = 1 - u;                       \/\/ 1 \u2192 0\n        return f * f;                          \/\/ ease-out\n      }\n\n      return 0; \/\/ na de hoofdwind gebruiken we micro-breeze los\n    }\n\n    function simulate() {\n      const now = performance.now() \/ 1000;\n      let dt = now - lastTime;\n      lastTime = now;\n      dt = Math.min(dt, 0.03);\n\n      const t = now - START_TIME;\n\n      let windFactor = getWindFactor(t);\n\n      \/\/ Gusts\n      const gust = 0.8 + 0.3 * Math.sin(t * 2.4);\n      windFactor *= gust;\n\n      const BASE_WIND = 950;\n\n      \/\/ Micro-breeze voor heel subtiele beweging na afloop\n      const microBreeze = 40 * Math.sin(t * 0.8);\n\n      const tailCount = Math.max(1, NUM_SEGMENTS - FIXED_COUNT);\n\n      for (let i = 0; i < points.length; i++) {\n        const p = points[i];\n\n        let windX = 0;\n        let gravity = 0;\n\n        if (i >= FIXED_COUNT) {\n          const tailFactor = (i - FIXED_COUNT) \/ (tailCount - 1 || 1);\n\n          const mainWind = BASE_WIND * windFactor * (0.4 + 0.6 * tailFactor);\n          let breeze = 0;\n\n          if (windFactor <= 0.01) {\n            \/\/ alleen nog zacht ademen in de staart\n            breeze = microBreeze * (0.2 + 0.8 * tailFactor);\n          }\n\n          windX = mainWind + breeze;\n          gravity = GRAVITY;\n        } else {\n          windX = 0;\n          gravity = 0;\n        }\n\n        p.applyVerlet(dt, windX, gravity);\n      }\n\n      \/\/ Constraints\n      for (let iter = 0; iter < CONSTRAINT_ITER; iter++) {\n        for (let i = 0; i < points.length - 1; i++) {\n          const p1 = points[i];\n          const p2 = points[i + 1];\n\n          let dx = p2.x - p1.x;\n          let dy = p2.y - p1.y;\n          const dist = Math.sqrt(dx * dx + dy * dy) || 0.0001;\n          const diff = (dist - SEGMENT_LENGTH) \/ dist;\n\n          const offsetX = dx * 0.5 * diff;\n          const offsetY = dy * 0.5 * diff;\n\n          if (!p1.pinned) {\n            p1.x += offsetX;\n            p1.y += offsetY;\n          }\n          if (!p2.pinned) {\n            p2.x -= offsetX;\n            p2.y -= offsetY;\n          }\n        }\n\n        points[0].x = anchorX;\n        points[0].y = anchorY;\n      }\n\n      \/\/ Basisdeel strikt horizontaal\n      for (let i = 0; i < FIXED_COUNT; i++) {\n        points[i].x = anchorX + i * SEGMENT_LENGTH;\n        points[i].y = anchorY;\n        points[i].oldx = points[i].x;\n        points[i].oldy = points[i].y;\n      }\n\n      \/\/ Staart niet uit beeld\n      for (let i = FIXED_COUNT; i < points.length; i++) {\n        const p = points[i];\n        if (p.y > height - 60) {\n          p.y = height - 60;\n        }\n      }\n\n      \/\/ ---- EINDVORM: omgekeerde Nike-swoosh ----\n      \/\/ Als de wind vrijwel weg is, trekken we de staart\n      \/\/ langzaam richting een zachte boog (meer kromming aan het einde).\n      const windAlmostGone = getWindFactor(t) <= 0.02;\n      if (windAlmostGone) {\n        const maxCurve = 55;          \/\/ hoeveel de staart omlaag buigt (px)\n        const relax = 0.02;           \/\/ hoe snel hij naar de boog toe beweegt\n\n        for (let i = FIXED_COUNT; i < NUM_SEGMENTS; i++) {\n          const p = points[i];\n          const idx = i - FIXED_COUNT;\n          const s = idx \/ (tailCount - 1 || 1); \/\/ 0..1 langs de staart\n\n          \/\/ X-doel: gewoon mooi horizontaal doorlopen\n          const targetX = anchorX + i * SEGMENT_LENGTH;\n\n          \/\/ Y-doel: omgekeerde Nike-vorm: weinig kromming aan begin,\n          \/\/ veel meer aan het einde (s^2 \u2192 meer buiging in de tip).\n          const curveOffset = maxCurve * Math.pow(s, 1.7);\n          const targetY = anchorY + curveOffset;\n\n          p.x += (targetX - p.x) * relax;\n          p.y += (targetY - p.y) * relax;\n        }\n      }\n    }\n\n    function draw() {\n      ctx.clearRect(0, 0, width, height);\n\n      const bg = ctx.createRadialGradient(\n        width * 0.35, height * 0.1, 0,\n        width * 0.5, height * 0.85, Math.max(width, height)\n      );\n      bg.addColorStop(0, \"rgba(15, 23, 42, 0.9)\");\n      bg.addColorStop(1, \"rgba(0, 0, 0, 1)\");\n      ctx.fillStyle = bg;\n      ctx.fillRect(0, 0, width, height);\n\n      const tail = points[points.length - 1];\n\n      \/\/ Schaduw \/ glow onder de lijn\n      ctx.save();\n      ctx.filter = \"blur(16px)\";\n      ctx.strokeStyle = \"rgba(15, 23, 42, 0.9)\";\n      ctx.lineWidth = 8;\n      ctx.beginPath();\n      ctx.moveTo(points[0].x, points[0].y + 4);\n      for (let i = 1; i < points.length; i++) {\n        ctx.lineTo(points[i].x, points[i].y + 4);\n      }\n      ctx.stroke();\n      ctx.restore();\n\n      \/\/ Lijn zelf (Studio Luw style)\n      const grad = ctx.createLinearGradient(\n        points[0].x, points[0].y,\n        tail.x, tail.y\n      );\n      grad.addColorStop(0, \"#e5e7eb\");\n      grad.addColorStop(0.4, \"#f97316\");\n      grad.addColorStop(1, \"#b45309\");\n\n      ctx.lineWidth = 4;\n      ctx.lineJoin = \"round\";\n      ctx.lineCap = \"round\";\n      ctx.strokeStyle = grad;\n      ctx.beginPath();\n      ctx.moveTo(points[0].x, points[0].y);\n      for (let i = 1; i < points.length; i++) {\n        ctx.lineTo(points[i].x, points[i].y);\n      }\n      ctx.stroke();\n\n      \/\/ Ankerpunt\n      ctx.beginPath();\n      ctx.arc(anchorX, anchorY, 4, 0, Math.PI * 2);\n      ctx.fillStyle = \"#22c55e\";\n      ctx.fill();\n    }\n\n    function loop() {\n      simulate();\n      draw();\n      requestAnimationFrame(loop);\n    }\n\n    loop();\n\n    window.addEventListener('resize', () => {\n      width = window.innerWidth;\n      height = window.innerHeight;\n      canvas.width = width;\n      canvas.height = height;\n    });\n  <\/script>\n<\/body>\n<\/html>\n\n","protected":false},"excerpt":{"rendered":"<p>Studio Luw \u2013 Streep met Swoosh Staart STUDIO LUW STREEP \u2022 SWOOSH STAART WIND \u2192 NADEINEN \u2192 EINDIGT IN OMGEKEERDE NIKE-KROMMING<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-66","page","type-page","status-publish","hentry"],"blocksy_meta":[],"_links":{"self":[{"href":"https:\/\/jettdax.com\/index.php\/wp-json\/wp\/v2\/pages\/66","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jettdax.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/jettdax.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/jettdax.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jettdax.com\/index.php\/wp-json\/wp\/v2\/comments?post=66"}],"version-history":[{"count":13,"href":"https:\/\/jettdax.com\/index.php\/wp-json\/wp\/v2\/pages\/66\/revisions"}],"predecessor-version":[{"id":82,"href":"https:\/\/jettdax.com\/index.php\/wp-json\/wp\/v2\/pages\/66\/revisions\/82"}],"wp:attachment":[{"href":"https:\/\/jettdax.com\/index.php\/wp-json\/wp\/v2\/media?parent=66"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}