PORTFOLIO WEB

Este proyecto consistió en el desarrollo del portfolio web de Nacho Dumanski, editor de video, con el objetivo de construir una experiencia digital alineada con su identidad como creador.

El desafío no era solo mostrar proyectos, sino traducir el universo de trabajo del editor en una experiencia interactiva coherente.

Para ello, se tomaron decisiones basadas en su entorno cotidiano: la navegación se inspira en una línea de tiempo de edición, los cursores replican los utilizados en software profesional y la barra de scroll funciona como un timeline, reforzando la metáfora del proceso de edición.

Cada proyecto fue tratado como una pieza independiente, evitando estructuras repetitivas y permitiendo que cada uno construya su propia narrativa visual.

Como elemento central, se desarrolló un modelo 3D interactivo con las iniciales del artista, utilizado en el banner principal para generar un impacto visual inmediato.

El resultado es un portfolio que no solo presenta trabajos, sino que interpreta la forma en que el autor los construye, convirtiendo la navegación en una extensión de su lenguaje creativo.

SITE MAP

ELEMENTOS PERSONALIZADOS

Slide – Timeline Premiere

Grilla / Contenedores 

BUSINESS CARD

No se trata solo de contar algo, sino de cómo lo contamos. Esa diferencia es la que construye identidad. Por eso desarrollamos esta tarjeta de presentación interactiva, pensada para destacar y representar a nuestro cliente de forma única.

				
					<div class="aure0-rope" style="height:0px; width:100%;" 
     data-img="https://aure0.com/wp-content/uploads/2026/04/NachoCard.png"
    data-img-width="300"
     data-rope-length="350"
     data-rope-color="#e73d33"
    data-rope-width="10"
     data-x=50>
</div>

				
			
				
					<style>
/* Minimal styles */
.aure0-rope {
  position: relative;
  width: 100%;
  margin: 0 auto;
}

.aure0-rope .matter-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
}

.aure0-rope .rope-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 2;
  pointer-events: none;
}

.aure0-rope img {
  position: absolute;
  transform: translate(-50%, -50%);
  will-change: left, top, transform;
  pointer-events: none;
  -webkit-user-drag: none;
  user-select: none;
  filter: drop-shadow(-30px 30px 20px #00000070);
}
</style>

<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>

<script>
document.addEventListener("DOMContentLoaded", () => {

  /* -----------------------------------------------------------
     VARIABLES DE CONTROL GLOBAL
     ----------------------------------------------------------- */

  const ropeStiffness = 0.005;
  const ropeDamping = 0.03;
  const ropeAnimDuration = 500;
  const ropeEase = p => 1 - (1 - p) * (1 - p);

  /* Detectar mobile / touch */
  const isMobile =
  window.matchMedia("(max-width: 768px)").matches &&
  /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);

  const {
    Engine, Render, Runner, Bodies,
    Composite, Constraint, Mouse,
    MouseConstraint, Body, Events
  } = Matter;

  document.querySelectorAll(".aure0-rope").forEach(container => {

    const imgURL = container.dataset.img;
    const ropeLengthTarget = parseInt(container.dataset.ropeLength || "220", 10);
    const ropeColor = container.dataset.ropeColor || "#e73d33";
    const imgW = parseInt(container.dataset.imgWidth || "200", 10);
    const ropeLineWidth = parseFloat(container.dataset.ropeWidth || "6");

    const img = document.createElement("img");
    img.src = imgURL;
    img.style.width = imgW + "px";
    img.draggable = false;
    container.appendChild(img);

    img.onload = () => {

      const imgH = img.naturalHeight * (imgW / img.naturalWidth);
      const totalHeight = ropeLengthTarget + imgH + 10;
      container.style.height = totalHeight + "px";

      const ropeCanvas = document.createElement("canvas");
      ropeCanvas.className = "rope-canvas";
      container.appendChild(ropeCanvas);

      const width = container.clientWidth;
      const height = totalHeight;

      const engine = Engine.create();
      engine.world.gravity.y = 1;

      const render = Render.create({
        element: container,
        engine,
        options: {
          width,
          height,
          wireframes: false,
          background: "transparent",
          showCollisions: false
        }
      });

      Render.run(render);
      Runner.run(Runner.create(), engine);

      render.canvas.classList.add("matter-canvas");

      const ctx = ropeCanvas.getContext("2d");
      const dpr = window.devicePixelRatio || 1;
      ropeCanvas.width = width * dpr;
      ropeCanvas.height = height * dpr;
      ropeCanvas.style.width = width + "px";
      ropeCanvas.style.height = height + "px";
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);

      const posX = (width * (parseFloat(container.dataset.x || "50"))) / 100;

      const startY = 30 + imgH / 2;
      const box = Bodies.rectangle(posX, startY, imgW, imgH, {
        frictionAir: 0.03,
        restitution: 0.02,
        render: { visible: false }
      });

      Composite.add(engine.world, box);

      const rope = Constraint.create({
        pointA: { x: posX, y: 0 },
        bodyB: box,
        length: 40,
        stiffness: ropeStiffness,
        damping: ropeDamping,
        render: { visible: false }
      });

      Composite.add(engine.world, rope);

      /* INTERACCIÓN SOLO EN DESKTOP */
      if (!isMobile) {
        const mouse = Mouse.create(render.canvas);
        const mouseConstraint = MouseConstraint.create(engine, {
          mouse,
          constraint: {
            stiffness: 0.015,
            damping: 0.2,
            render: { visible: false }
          }
        });
        Composite.add(engine.world, mouseConstraint);
      }

      function drawRope() {
        ctx.clearRect(0, 0, width, height);

        const p0 = rope.pointA;
        const p1 = { x: box.position.x, y: box.position.y - imgH / 2 };

        ctx.lineWidth = ropeLineWidth;
        ctx.strokeStyle = ropeColor;
        ctx.lineCap = "round";

        const midX = (p0.x + p1.x) / 2;
        const curve = Math.min(120, Math.abs(p1.y - p0.y) * 0.4 + 40);

        ctx.beginPath();
        ctx.moveTo(p0.x, p0.y);
        ctx.bezierCurveTo(
          midX, p0.y + curve,
          midX, p1.y - curve * 0.6,
          p1.x, p1.y
        );
        ctx.stroke();
      }

      Events.on(engine, "afterUpdate", () => {
        img.style.left = box.position.x + "px";
        img.style.top = box.position.y + "px";
        img.style.transform =
          `translate(-50%, -50%) rotate(${box.angle}rad)`;
        drawRope();
      });

      function animateRopeLength(from, to) {
        const start = performance.now();
        function tick(t) {
          const p = Math.min(1, (t - start) / ropeAnimDuration);
          rope.length = from + (to - from) * ropeEase(p);
          if (p < 1) requestAnimationFrame(tick);
        }
        requestAnimationFrame(tick);
      }

      const observer = new IntersectionObserver(entries => {
        if (entries[0].isIntersecting) {
          animateRopeLength(40, ropeLengthTarget);
          Body.applyForce(box, box.position, {
            x: (Math.random() - 0.5) * 0.004,
            y: 0.015
          });
          observer.disconnect();
        }
      }, { threshold: 0.35 });

      observer.observe(container);

      if (!isMobile) {
		 window.addEventListener("resize", () => location.reload());
	  };
    };
  });
});
</script>

				
			

Démosle forma a tu idea

Cada proyecto comienza con una intuición. Queremos entender la tuya para dar una estructura visual con propósito, claridad y coherencia.