{"id":512,"date":"2026-05-21T18:52:05","date_gmt":"2026-05-21T16:52:05","guid":{"rendered":"https:\/\/robertoverkamp.com\/?page_id=512"},"modified":"2026-05-21T19:15:02","modified_gmt":"2026-05-21T17:15:02","slug":"landingpage-code","status":"publish","type":"page","link":"https:\/\/robertoverkamp.com\/?page_id=512","title":{"rendered":"Landingpage-Code"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"512\" class=\"elementor elementor-512\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-c0ccb56 e-con-full e-flex e-con e-parent\" data-id=\"c0ccb56\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-32681fd elementor-widget elementor-widget-html\" data-id=\"32681fd\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style>\n  html, body {\n    margin: 0;\n    padding: 0;\n    overflow: hidden;\n    background: #fff;\n  }\n  \n  canvas {\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 100vw;\n  height: 100vh;\n  display: block;\n  z-index: 0;\n}\n\nheader {\n  position: relative;\n  z-index: 10;\n}\n\n\n<\/style>\n\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/p5.js\/1.9.0\/p5.min.js\"><\/script>\n\n<script>\nlet tiles = [];\nconst numTiles = 7;\nconst BASE_SIZE = 130;\nlet imgs = [];\nlet customFont;\nlet customFontItalic;\nlet bombs = [];\n\nlet titleText;\n\nfunction preload() {\n  for (let i = 0; i < numTiles; i++) {\n    imgs.push(loadImage(`https:\/\/picsum.photos\/200?random=${i + 1}`));\n  }\n}\n\nfunction setup() {\n  createCanvas(windowWidth, windowHeight);\n  imageMode(CENTER); \n  \n  const tileTitles = ['Exploration', 'Struktur', 'Material', 'Komposition', 'Rhythmus', 'Farbe', 'Typografie', 'Form', 'Bewegung', 'Kontrast'];\n  const tileSubtitles = ['Visuelle Studie', 'Geordnete Fl\u00e4che', 'Haptisches Spiel', 'Bildaufbau', 'Dynamik', 'Farbwelt', 'Schriftklang', 'Struktur', 'Flow', 'Spannung'];\n\n  randomSeed(123456);\n  const centerX = width \/ 2;\n  const centerY = height \/ 2;\n  for (let i = 0; i < numTiles; i++) {\n    let targetX = random(BASE_SIZE \/ 2, width - BASE_SIZE \/ 2);\n    let targetY = random(BASE_SIZE \/ 2, height - BASE_SIZE \/ 2);\n    tiles.push(new Tile(targetX, targetY, BASE_SIZE, imgs[i], tileTitles[i], tileSubtitles[i], centerX, centerY));\n  }\n\n  titleText = new TitleText('Robert Overkamp', 0, 0, customFontItalic, 50);\n}\n\nfunction draw() {\n  background(255);\n\n  push();\n  stroke(0);\n  strokeWeight(2);\n  line(0, height - 40, width, height - 40);\n  pop();\n\n  for (let i = bombs.length - 1; i >= 0; i--) {\n    bombs[i].update();\n    bombs[i].display();\n    if (bombs[i].done) {\n      bombs.splice(i, 1);\n    }\n  }\n\n  let overImage = false;\n  for (let tile of tiles) {\n    tile.update();\n    if (tile.isMouseOver()) {\n      overImage = true;\n    }\n  }\n  titleText.update();\n\n  cursor(overImage ? HAND : ARROW);\n\n  checkCollisions();\n\n  for (let tile of tiles) {\n    tile.display();\n  }\n  titleText.display();\n}\n\nfunction keyPressed() {\n  if (key === ' ') { \n    for (let tile of tiles) {\n      tile.reset();\n    }\n    titleText.reset();\n    bombs = [];\n  }\n}\n\nclass Tile {\n  constructor(targetX, targetY, baseSize, img, title, subtitle, startX, startY) {\n    this.targetX = targetX;\n    this.targetY = targetY;\n    this.startX = startX;\n    this.startY = startY;\n    this.x = this.startX;\n    this.y = this.startY;\n    this.baseSize = baseSize;\n    this.size = baseSize;\n    this.targetSize = baseSize;\n    this.img = img;\n    this.title = title;\n    this.subtitle = subtitle;\n    \n    this.animationElapsed = 0;\n    this.animationDuration = 300;\n    this.animating = true;\n    \n    this.vx = 0;\n    this.vy = 0;\n    \n    this.isDragging = false;\n    this.offsetX = 0;\n    this.offsetY = 0;\n    this.prevMouseX = null;\n    this.prevMouseY = null;\n    this.dragVx = 0;\n    this.dragVy = 0;\n  }\n\n  \/\/ ==========================================\n  \/\/ HIER GE\u00c4NDERT: Kacheln fliegen von der aktuellen Position zur\u00fcck\n  \/\/ ==========================================\n  reset() {\n    \/\/ Setzt den Startpunkt der Animation auf die aktuelle Position\n    this.startX = this.x; \n    this.startY = this.y;\n    \n    this.vx = 0;\n    this.vy = 0;\n    this.isDragging = false;\n    this.animating = true;\n    this.animationElapsed = 0; \/\/ Startet den Animations-Timer neu\n  }\n\n  update() {\n    if (this.isMouseOver()) {\n      this.targetSize = 150;\n    } else {\n      this.targetSize = this.baseSize;\n    }\n\n    this.size += (this.targetSize - this.size) * 0.15;\n    let radius = this.size \/ 2;\n\n    if (this.animating) {\n      this.animationElapsed += deltaTime;\n      let t = constrain(this.animationElapsed \/ this.animationDuration, 0, 1);\n      t = 1 - pow(1 - t, 3);\n      this.x = lerp(this.startX, this.targetX, t);\n      this.y = lerp(this.startY, this.targetY, t);\n      if (this.animationElapsed >= this.animationDuration) {\n        this.animating = false;\n        this.x = this.targetX;\n        this.y = this.targetY;\n      }\n      return;\n    }\n\n    if (this.isDragging) {\n      this.x = mouseX + this.offsetX;\n      this.y = mouseY + this.offsetY;\n      \n      if (this.prevMouseX !== null && this.prevMouseY !== null) {\n        this.dragVx = (mouseX - this.prevMouseX) * 1.2;\n        this.dragVy = (mouseY - this.prevMouseY) * 1.2;\n      }\n      this.prevMouseX = mouseX;\n      this.prevMouseY = mouseY;\n\n      this.vx = this.dragVx;\n      this.vy = this.dragVy;\n    } else {\n      this.x += this.vx;\n      this.y += this.vy;\n\n      this.vx *= 0.999;\n      this.vy *= 0.999;\n    }\n\n    if (this.x - radius < 0) {\n      this.x = radius;\n      this.vx *= -0.6;\n    }\n    if (this.x + radius > width) {\n      this.x = width - radius;\n      this.vx *= -0.6;\n    }\n\n    if (this.y - radius < 0) {\n      this.y = radius;\n      this.vy *= -0.6;\n    }\n    const bottomLimit = height - 40;\n    if (this.y + radius > bottomLimit) {\n      this.y = bottomLimit - radius;\n      this.vy *= -0.6;\n    }\n  }\n\n  display() {\n    image(this.img, this.x, this.y, this.size, this.size);\n\n    if (this.isMouseOver()) {\n      push();\n      textAlign(CENTER, TOP);\n      fill(0);\n      noStroke();\n      if(customFont) textFont(customFont);\n      textStyle(NORMAL);\n      textSize(16);\n      text(this.title, this.x, this.y + this.size \/ 2 + 8);\n      if(customFontItalic) textFont(customFontItalic);\n      textStyle(ITALIC);\n      textSize(14);\n      text(this.subtitle, this.x, this.y + this.size \/ 2 + 28);\n      pop();\n    }\n  }\n\n  isMouseOver() {\n    let radius = this.size \/ 2;\n    return mouseX >= this.x - radius && mouseX <= this.x + radius &&\n           mouseY >= this.y - radius && mouseY <= this.y + radius;\n  }\n}\n\nclass TitleText {\n  constructor(text, left, top, font, fontSize) {\n    this.text = text;\n    this.font = font;\n    this.fontSize = fontSize;\n    this.isDragging = false;\n    this.isStatic = true;\n    this.offsetX = 0;\n    this.offsetY = 0;\n    this.padding = 16;\n\n    this.baseX = left;\n    this.baseY = top;\n    this.textLines = [];\n    this.textWidth = 0;\n    this.textHeight = 0;\n    this.blockW = 0;\n    this.blockH = 0;\n    this.maxW = width \/ 3;\n    this.x = this.baseX;\n    this.y = this.baseY;\n  }\n\n  reset() {\n    this.x = this.baseX;\n    this.y = this.baseY;\n    this.isDragging = false;\n  }\n\n  update() {\n    this.maxW = width \/ 3;\n    this.calcTextBounds();\n\n    if (this.isDragging) {\n      this.x = mouseX + this.offsetX;\n      this.y = mouseY + this.offsetY;\n    }\n\n    if (this.x < 0) this.x = 0;\n    if (this.x + this.blockW > width) this.x = width - this.blockW;\n    if (this.y < 0) this.y = 0;\n    if (this.y + this.blockH > height) this.y = height - this.blockH;\n  }\n\n  calcTextBounds() {\n    if(this.font) textFont(this.font);\n    textSize(this.fontSize);\n    textAlign(LEFT, TOP);\n\n    const availableWidth = this.maxW - this.padding * 2;\n    this.textLines = this.wrapText(this.text, availableWidth);\n\n    this.textWidth = 0;\n    for (let line of this.textLines) {\n      this.textWidth = max(this.textWidth, textWidth(line));\n    }\n\n    this.lineHeight = textAscent() + textDescent() + 4;\n    this.textHeight = this.textLines.length * this.lineHeight;\n    this.blockW = this.textWidth + this.padding * 2;\n    this.blockH = this.textHeight + this.padding * 2;\n  }\n\n  wrapText(txt, maxWidth) {\n    const words = txt.split(' ');\n    const lines = [];\n    let current = '';\n\n    for (let word of words) {\n      const test = current ? current + ' ' + word : word;\n      if (textWidth(test) <= maxWidth) {\n        current = test;\n      } else {\n        if (current) lines.push(current);\n        current = word;\n      }\n    }\n    if (current) lines.push(current);\n    return lines;\n  }\n\n  display() {\n    push();\n    if(this.font) textFont(this.font);\n    textSize(this.fontSize);\n    textAlign(LEFT, TOP);\n\n    fill(0);\n    noStroke();\n    let ty = this.y + this.padding;\n    for (let line of this.textLines) {\n      text(line, this.x + this.padding, ty);\n      ty += this.lineHeight;\n    }\n    pop();\n  }\n\n  isMouseOver() {\n    return mouseX >= this.x && mouseX <= this.x + this.blockW &&\n           mouseY >= this.y && mouseY <= this.y + this.blockH;\n  }\n}\n\nfunction checkCollisions() {\n  for (let i = 0; i < tiles.length; i++) {\n    for (let j = i + 1; j < tiles.length; j++) {\n      if (tiles[i].animating || tiles[j].animating) continue;\n      resolveCollision(tiles[i], tiles[j]);\n    }\n  }\n\n  for (let tile of tiles) {\n    if (tile.animating) continue;\n    resolveCollision(tile, titleText);\n  }\n}\n\nfunction resolveCollision(a, b) {\n  let aLeft, aRight, aTop, aBottom;\n  let bLeft, bRight, bTop, bBottom;\n  let aCx, aCy, bCx, bCy;\n\n  if (a.size) {\n    let half = a.size \/ 2;\n    aLeft = a.x - half; aRight = a.x + half; aTop = a.y - half; aBottom = a.y + half;\n    aCx = a.x; aCy = a.y;\n  } else {\n    aLeft = a.x; aRight = a.x + a.blockW; aTop = a.y; aBottom = a.y + a.blockH;\n    aCx = a.x + a.blockW \/ 2; aCy = a.y + a.blockH \/ 2;\n  }\n\n  if (b.size) {\n    let half = b.size \/ 2;\n    bLeft = b.x - half; bRight = b.x + half; bTop = b.y - half; bBottom = b.y + half;\n    bCx = b.x; bCy = b.y;\n  } else {\n    bLeft = b.x; bRight = b.x + b.blockW; bTop = b.y; bBottom = b.y + b.blockH;\n    bCx = b.x + b.blockW \/ 2; bCy = b.y + b.blockH \/ 2;\n  }\n\n  if (aLeft < bRight && aRight > bLeft && aTop < bBottom && aBottom > bTop) {\n    let overlapX = min(aRight, bRight) - max(aLeft, bLeft);\n    let overlapY = min(aBottom, bBottom) - max(aTop, bTop);\n\n    if (overlapX > 0 && overlapY > 0) {\n      let restitution = 0.7;\n      let axisX = overlapX < overlapY;\n\n      if (axisX) {\n        let sep = overlapX \/ 2 + 0.1;\n        let normal = aCx < bCx ? 1 : -1;\n        if (aCx < bCx) {\n          if (!a.isDragging && !a.isStatic) a.x -= sep;\n          if (!b.isDragging && !b.isStatic) b.x += sep;\n          if (a.isStatic && !b.isDragging) b.x += sep;\n          if (b.isStatic && !a.isDragging) a.x -= sep;\n        } else {\n          if (!a.isDragging && !a.isStatic) a.x += sep;\n          if (!b.isDragging && !b.isStatic) b.x -= sep;\n          if (a.isStatic && !b.isDragging) b.x -= sep;\n          if (b.isStatic && !a.isDragging) a.x += sep;\n        }\n\n        let aVx = a.vx || 0;\n        let bVx = b.vx || 0;\n        let relVel = (bVx - aVx) * normal;\n        if (relVel < 0) {\n          let impulse = -(1 + restitution) * relVel \/ 2;\n          if (!a.isDragging && !a.isStatic) a.vx -= impulse * normal;\n          if (!b.isDragging && !b.isStatic) b.vx += impulse * normal;\n          if (a.isStatic && !b.isDragging) b.vx += impulse * normal;\n          if (b.isStatic && !a.isDragging) a.vx -= impulse * normal;\n        }\n      } else {\n        let sep = overlapY \/ 2 + 0.1;\n        let normal = aCy < bCy ? 1 : -1;\n        if (aCy < bCy) {\n          if (!a.isDragging && !a.isStatic) a.y -= sep;\n          if (!b.isDragging && !b.isStatic) b.y += sep;\n          if (a.isStatic && !b.isDragging) b.y += sep;\n          if (b.isStatic && !a.isDragging) a.y -= sep;\n        } else {\n          if (!a.isDragging && !a.isStatic) a.y += sep;\n          if (!b.isDragging && !b.isStatic) b.y -= sep;\n          if (a.isStatic && !b.isDragging) b.y -= sep;\n          if (b.isStatic && !a.isDragging) a.y += sep;\n        }\n\n        let aVy = a.vy || 0;\n        let bVy = b.vy || 0;\n        let relVel = (bVy - aVy) * normal;\n        if (relVel < 0) {\n          let impulse = -(1 + restitution) * relVel \/ 2;\n          if (!a.isDragging && !a.isStatic) a.vy -= impulse * normal;\n          if (!b.isDragging && !b.isStatic) b.vy += impulse * normal;\n          if (a.isStatic && !b.isDragging) b.vy += impulse * normal;\n          if (b.isStatic && !a.isDragging) a.vy -= impulse * normal;\n        }\n      }\n    }\n  }\n}\n\nfunction mousePressed() {\n  let clickedTile = false;\n  for (let i = tiles.length - 1; i >= 0; i--) {\n    if (tiles[i].isMouseOver()) {\n      clickedTile = true;\n      tiles[i].isDragging = true;\n      tiles[i].offsetX = tiles[i].x - mouseX;\n      tiles[i].offsetY = tiles[i].y - mouseY;\n      tiles[i].prevMouseX = mouseX;\n      tiles[i].prevMouseY = mouseY;\n      tiles[i].dragVx = 0;\n      tiles[i].dragVy = 0;\n\n      let grabbed = tiles.splice(i, 1)[0];\n      tiles.push(grabbed);\n      break;\n    }\n  }\n\n  if (!clickedTile) {\n    bombs.push(new Bomb(mouseX, mouseY, 300));\n  }\n}\n\nfunction mouseReleased() {\n  for (let tile of tiles) {\n    if (tile.isDragging) {\n      tile.isDragging = false;\n      tile.vx = tile.dragVx * 1.2;\n      tile.vy = tile.dragVy * 1.2;\n      tile.prevMouseX = null;\n      tile.prevMouseY = null;\n    }\n  }\n}\n\nclass Bomb {\n  constructor(x, y, delay) {\n    this.x = x;\n    this.y = y;\n    this.delay = delay;\n    this.created = millis();\n    this.done = false;\n    this.exploded = false;\n  }\n\n  update() {\n    const elapsed = millis() - this.created;\n    if (!this.exploded && elapsed >= this.delay) {\n      this.explode();\n      this.exploded = true;\n      this.done = true;\n    }\n  }\n\n  display() {\n    const elapsed = millis() - this.created;\n    const remaining = max(0, this.delay - elapsed);\n    push();\n    noFill();\n    stroke(0);\n    strokeWeight(2);\n    ellipse(this.x, this.y, 32);\n    line(this.x, this.y - 12, this.x, this.y - 24);\n    fill(0);\n    noStroke();\n    textAlign(CENTER, CENTER);\n    textSize(12);\n    text(nf(remaining \/ 1000, 1, 1), this.x, this.y);\n    pop();\n  }\n\n  explode() {\n    const strength = 30;\n    const explosionRadius = 500;\n    for (let tile of tiles) {\n      const dx = tile.x - this.x;\n      const dy = tile.y - this.y;\n      const dist = sqrt(dx * dx + dy * dy);\n      if (dist > explosionRadius) continue;\n      const force = strength * (1 - dist \/ explosionRadius) \/ explosionRadius;\n      tile.vx += dx * force * 2;\n      tile.vy += dy * force * 2;\n      tile.isDragging = false;\n      tile.prevMouseX = null;\n      tile.prevMouseY = null;\n    }\n  }\n}\n\nfunction windowResized() {\n  resizeCanvas(windowWidth, windowHeight);\n  for (let t of tiles) {\n    let radius = t.size \/ 2;\n    t.x = constrain(t.x, radius, width - radius);\n    t.y = constrain(t.y, radius, height - radius);\n  }\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-512","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/robertoverkamp.com\/index.php?rest_route=\/wp\/v2\/pages\/512","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/robertoverkamp.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/robertoverkamp.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/robertoverkamp.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/robertoverkamp.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=512"}],"version-history":[{"count":25,"href":"https:\/\/robertoverkamp.com\/index.php?rest_route=\/wp\/v2\/pages\/512\/revisions"}],"predecessor-version":[{"id":543,"href":"https:\/\/robertoverkamp.com\/index.php?rest_route=\/wp\/v2\/pages\/512\/revisions\/543"}],"wp:attachment":[{"href":"https:\/\/robertoverkamp.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=512"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}