Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

158
Vistas
Columna dividida (50% cada una) desplazándose en la dirección opuesta - continuo/bucle

EDITAR En una inspección más cercana, esto está bastante roto al cambiar el tamaño de la ventana gráfica. Deja de funcionar o en ventanas estrechas y el scroll es súper súper rápido. ¡Así que lo ofrezco como recompensa!

--

He visto algo similar y he estado tratando de refactorizar el JS. Tengo dos columnas de contenido, que cuando se desplazan se mueven en direcciones opuestas. Esto debería repetirse continuamente.

El problema es si elimino la altura del elemento .project . El contenido se desplazará suavemente cuando se desplace hacia abajo pero no hacia arriba. La altura/longitud del contenido variará, por lo que realmente no puedo tener un valor fijo aquí.

Esto parece depender de la altura de la ventana gráfica. Si la interfaz de usuario se comporta según lo previsto y reduzco el ancho de la ventana gráfica, puede dejar de funcionar como se describe anteriormente. Pero si luego reduzco la altura, puede comenzar a comportarse correctamente nuevamente. Entonces, ¿tal vez se deba a la cantidad de contenido visible en la ventana gráfica en carga?

Ejemplo (también en fragmento de código): https://jsfiddle.net/rdowb0y5/1

Agregaré una 'consulta de medios' para que esto solo sea visible en las vistas de tableta/escritorio y en dispositivos móviles, el JS se elimina y el contenido simplemente se apila.

Gracias de antemano, ¡realmente espero algún apoyo en esto!

 $(document).ready(function() { var num_children=$('.split-loop__left').children().length; var child_height=$('.split-loop__right').height() / num_children; var half_way=num_children * child_height / 2; $(window).scrollTop(half_way); function crisscross() { var parent=$(".split-loop"); //.first(); var clone=$(parent).clone(); var leftSide=$(clone).find('.split-loop__left'); var rightSide=$(clone).find('.split-loop__right'); if (window.scrollY > half_way) { //We are scrolling up $(window).scrollTop(half_way - child_height); var firstLeft=$(leftSide).children().first(); var lastRight=$(rightSide).children().last(); lastRight.appendTo(leftSide); firstLeft.prependTo(rightSide); } else if (window.scrollY < half_way - child_height) { var lastLeft=$(leftSide).children().last(); var firstRight=$(rightSide).children().first(); $(window).scrollTop(half_way); lastLeft.appendTo(rightSide); firstRight.prependTo(leftSide); } $(leftSide).css('bottom', '-'+ window.scrollY + 'px'); $(rightSide).css('bottom', '-'+ window.scrollY + 'px'); $(parent).replaceWith(clone); } $(window).scroll(crisscross); } );
 /* Hide Scroll Bars */ ::-webkit-scrollbar { display: none; } html, body { margin: 0; padding: 0; -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ } /* Basic Styling */ img { border: 1px solid black; margin-bottom: 24px; width: 100%; max-width: 100%; } h2 { font-size: 14px; font-weight: normal; margin-bottom: 4px; font-family: 'Inter', sans-serif; } p { color: black; font-size: 11px; font-family: 'Inter', sans-serif; } /* Content will be in these eventually */ .bar-left, .bar-right { border-right: 1px solid black; box-sizing: border-box; height: 100vh; position: fixed; top: 0; left: 0; width: 48px; z-index: 10000; } .bar-right { border: none; border-left: 1px solid black; left: auto; right: 0; } /* Split Loop */ .split-loop { position: relative; margin: 0 48px; } .split-loop__left { // position: absolute; // left: 0%; // top: 0%; // right: auto; // bottom: auto; // z-index: 4; width: 50%; } .split-loop__right { border-left: 1px solid black; box-sizing: border-box; position: fixed; right: 48px; bottom: 0; z-index: 5; width: calc(50% - 48px); } .project { box-sizing: border-box; border-bottom: 1px solid black; height: 600px; padding: 48px; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script> <header class="bar-left"> </header> <div class="bar-right"> </div> <div class="view"> <div class="grid split-loop"> <div class="split-loop__left"> <div class="grid__item project"> <img src="https://www.fillmurray.com/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title</h2> <p class="project__desc">Short Description</p> </div> <div class="grid__item project"> <img src="https://www.fillmurray.com/g/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title</h2> <p class="project__desc">Short Description</p> </div> <div class="grid__item project"> <img src="https://www.fillmurray.com/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title</h2> <p class="project__desc">Short Description</p> </div> </div> <div class="split-loop__right"> <div class="grid__item project"> <img src="https://www.fillmurray.com/g/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title</h2> <p class="project__desc">Short Description</p> </div> <div class="grid__item project"> <img src="https://www.fillmurray.com/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title</h2> <p class="project__desc">Short Description</p> </div> <div class="grid__item project"> <img src="https://www.fillmurray.com/g/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title</h2> <p class="project__desc">Short Description</p> </div> </div> </div> </div>

over 4 years ago · Santiago Trujillo
2 Respuestas
Responde la pregunta

0

Familytype.co tiene las siguientes restricciones:

  1. Tiene que haber un contenedor de desplazamiento más. Están usando la barra de desplazamiento en el cuerpo.
  2. Ambos carriles deben tener la misma altura. Sus toboganes tienen diferentes alturas pero todos son múltiplos de 50vh. Y lograron tener carriles de la misma altura. Las alturas aún se basan en las ventanas gráficas; vea sus reglas de estilo .block--50 y .block--100 .
  3. Debe repetir los dos primeros elementos en el mismo carril para ver un cambio suave a la primera diapositiva.

En Siguiente, estilo familytype.co, enfoque, he repetido los dos primeros elementos en el carril izquierdo. Y luego clonó todos los elementos del carril izquierdo a la derecha en javascript. También se agregó una consulta de medios para mostrar solo un carril si el ancho de la ventana gráfica es inferior a 576 px.

 let view, parent, leftSide, rightSide; $(document).ready(function() { view = $('#view'); parent = $(".split-loop"); leftSide = $(parent).find('.split-loop__left'); rightSide = $(parent).find('.split-loop__right'); centerScrollPosition(); window.onresize = function(event) { centerScrollPosition(); }; //repeat first 2 $(leftSide).append($($(leftSide).children()[0]).clone()); $(leftSide).append($($(leftSide).children()[1]).clone()); //clone left lane to right let arr = []; $(leftSide).children().each((index, elm) => { arr.push($(elm).clone()); }); //shift right lane by half //arr.push.apply(arr, arr.splice(0, arr.length / 2)); //arr.push(arr[0].clone()); //arr.push(arr[1].clone()); $(rightSide).append(arr); //listen to scroll events //incase you want movement to happen on user scroll $(view).add(window).scroll(function() { //$(view).on('wheel', function (event) { handleScroll(); }); handleScroll(); }); function centerScrollPosition() { //center the content var vh = $(window).height(); var lh = leftSide.height(); var half_way = (lh - vh) / 2; $(window).scrollTop(half_way); } function handleScroll() { var scroll = $(window).scrollTop(); var sh = $(leftSide).height() - window.innerHeight; if (scroll >= (sh - 1)) { $(window).scrollTop(2); } else if (scroll == 0) { $(window).scrollTop(sh - 1); } $(rightSide).css({ "transform": "translate3d(0, " + ((((sh) - scroll * 2) * -1)) + "px, 0)" }); }
 * { box-sizing: border-box; } ::-webkit-scrollbar { display: none; } html, body { margin: 0; padding: 0; height: 100%; width: 100%; background-color: white; } body { overflow-y: scroll } /* Basic Styling */ img { max-width: 90%; object-fit: fill; margin-top: 1rem; align-self: center; border: 1px solid black; } h2 { font-size: 14px; font-weight: normal; margin-bottom: 4px; font-family: 'Inter', sans-serif; text-align: center; } p { color: black; font-size: 11px; font-family: 'Inter', sans-serif; text-align: center; } /* center line for debugging */ .center { position: fixed; width: 100vw; top: 50%; left: 0%; z-index: 100000; transform: translateY(-50%); border-bottom: 2px dashed gray; } /* Content will be in these eventually */ .view { overflow: hidden; position: relative; width: 80vw; margin: 0 auto; } .bar-left, .bar-right { /*border-right: 1px solid black;*/ box-sizing: border-box; height: 100vh; position: fixed; top: 0; left: 0; width: 5vw; z-index: 10000; } .bar-right { border: none; /*border-left: 1px solid black;*/ left: auto; right: 0; } /* Split Loop */ .split-loop { position: relative; width: 100%; height: 100%; } .split-loop>div { max-width: 50%; float: left; height: auto; transform-style: preserve-3d; display: flex; flex-flow: column; } .split-loop__right { will-change: transform; perspective: 1000; backface-visibility: hidden; } .project { border: 1px solid black; min-height: auto; background-color: lightskyblue; display: flex; flex-flow: column nowrap; } @media(max-width: 576px) { .split-loop>div { max-width: 100%; } .split-loop__right { display: none; } }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <!-- span class="center"></span --> <header class="bar-left"></header> <div class="bar-right"></div> <div id=view class="view"> <div class="grid split-loop"> <div class="split-loop__left"> <div class="grid__item project"> <img src="https://www.fillmurray.com/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title 1</h2> <p class="project__desc">Short Description Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequuntur, id?Short Description Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequuntur, id?</p> </div> <div class="grid__item project"> <img src="https://www.placecage.com/c/460/300" alt="" class="project__media" /> <h2 class="project__title">Project Title 2</h2> <p class="project__desc">Short Description Lorem ipsum dolor sit amet.</p> </div> <div class="grid__item project"> <img src="https://www.placecage.com/g/155/300" alt="" class="project__media" /> <h2 class="project__title">Project Title 3</h2> <p class="project__desc">Short Description Lorem ipsum dolor sit amet consectetur, adipisicing elit. Aspernatur nostrum in obcaecati itaque explicabo voluptatibus corporis cumque praesentium eaque beatae!</p> </div> <div class="grid__item project"> <img src="https://www.placecage.com/140/100" alt="" class="project__media" /> <h2 class="project__title">Project Title 4</h2> <p class="project__desc">Short Description Lorem, ipsum dolor sit amet consectetur adipisicing elit. Dolores nobis eius, minus optio ab earum. Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto, at. Fuga nisi nulla laborum explicabo possimus repellendus amet quidem eos.</p> </div> <div class="grid__item project"> <img src="https://www.stevensegallery.com/460/300" alt="" class="project__media" /> <h2 class="project__title">Project Title 5</h2> <p class="project__desc">Short Description Lorem ipsum dolor sit amet consectetur adipisicing elit. Nihil?</p> </div> <div class="grid__item project"> <img src="https://www.fillmurray.com/g/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title 6</h2> <p class="project__desc">Short Description Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quia iste distinctio doloremque facere!</p> </div> </div> <div class="split-loop__right"> </div> </div> </div>

Se necesita una ubicación más inteligente en el segundo carril.


Respuesta anterior: en lugar de usar la barra de desplazamiento, podemos hacerlo con transform. La explicación está en el código:

 //variables to track left and right container scrolls var leftX = 0, rightX = 0; //how quickly shifts will happen //depends on how many images are in a column //range 1.0 - 5.0 var shiftingThreshold = 1.7; var isAnimating = false; var parent, leftSide, rightSide; $(document).ready(function () { parent = $(".split-loop"); leftSide = $(parent).find('.split-loop__left'); rightSide = $(parent).find('.split-loop__right'); centerScrollPosition(); window.onresize = function (event) { centerScrollPosition(); }; document.querySelector('.split-loop__left').addEventListener('transitionend', () => { isAnimating = false; //positive value means clockwise //negative means counter clockwise //remove if you don't want to run loop //handleScroll(-125); }); //listen to scroll events //incase you want movement to happen on user scroll $('#view').on('wheel', function (event) { handleScroll(event.originalEvent.deltaY); }); //initiate continuous loop setTimeout(function () { //handleScroll(-125); }, 3000); }); function centerScrollPosition() { //center the content var vh = $('#view').height(); var slh = $('.split-loop__left').height(); var half_way = (slh - vh) / 2; $('#view').scrollTop(half_way); } function handleScroll(deltaY) { //handle next scroll event only after last transition ends. if (isAnimating) { return; } isAnimating = true; var isUpShifted = true; var isJumped = true; leftX -= deltaY; rightX += deltaY; if (deltaY !== 0) { var childHeight = $('.split-loop__left').children().first().height(); if ((leftX + childHeight / shiftingThreshold) <= 0) { //clockwise shift isUpShifted = true; var firstLeft = $(leftSide).children().first(); var lastRight = $(rightSide).children().last(); //shift left-first to right firstLeft.prependTo(rightSide); //shift right-last to left lastRight.appendTo(leftSide); leftX += childHeight + deltaY; rightX -= childHeight + deltaY; } else if ((leftX - childHeight / shiftingThreshold) > 0) { //anti clockwise shift isUpShifted = false; var lastLeft = $(leftSide).children().last(); var firstRight = $(rightSide).children().first(); //shift right-first to left firstRight.prependTo(leftSide); //shift left-last to right lastLeft.appendTo(rightSide); leftX -= childHeight - deltaY; rightX += childHeight - deltaY; } else { isJumped = false; } //if we've moved projects to other container then //adjust position before smooth scrolling effect if (isJumped) { $('.split-loop__left').css('transform', 'translateY(' + leftX + 'px)'); $('.split-loop__right').css('transform', 'translateY(' + rightX + 'px)'); $('.split-loop__left').css('transition-duration', '0s'); $('.split-loop__right').css('transition-duration', '0s'); if (isUpShifted) { leftX -= deltaY; rightX += deltaY; } else { leftX -= deltaY; rightX += deltaY; } } //do smooth scroll effect setTimeout(function () { $('.split-loop__left').css('transition-duration', '1.5s'); $('.split-loop__right').css('transition-duration', '1.5s'); $('.split-loop__left').css('transform', 'translateY(' + leftX + 'px)'); $('.split-loop__right').css('transform', 'translateY(' + rightX + 'px)'); }, 5); } }
 * { box-sizing: border-box; } html, body { margin: 0; padding: 0; height: 100%; width: 100%; background-color: wheat; } /* Basic Styling */ img { max-width: 80%; aspect-ratio: 6/4; object-fit: fill; margin-top: 10%; align-self: center; border: 1px solid black; } h2 { font-size: 14px; font-weight: normal; margin-bottom: 4px; font-family: 'Inter', sans-serif; text-align: center; } p { color: black; font-size: 11px; font-family: 'Inter', sans-serif; text-align: center; } /* center line for debugging */ .center { position: fixed; width: 100vw; top: 50%; left: 0%; z-index: 100000; transform: translateY(-50%); border-bottom: 2px dashed gray; } /* Content will be in these eventually */ .view { overflow: hidden; position: relative; width: 80vw; height: 100vh; margin: 0 auto; } .bar-left, .bar-right { /*border-right: 1px solid black;*/ box-sizing: border-box; height: 100vh; position: fixed; top: 0; left: 0; width: 5vw; z-index: 10000; } .bar-right { border: none; /*border-left: 1px solid black;*/ left: auto; right: 0; } /* Split Loop */ .split-loop { position: relative; width: 100%; height: 100%; } .split-loop>div { max-width: 50%; float: left; transition-timing-function: linear; height: auto; will-change: transform; } .project { border: 1px solid black; min-height: 70vh; background-color: lightskyblue; display: flex; flex-flow: column nowrap; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <!-- span class="center"></span --> <header class="bar-left"></header> <div class="bar-right"></div> <div id=view class="view"> <div class="grid split-loop"> <div class="split-loop__left"> <div class="grid__item project"> <img src="https://www.fillmurray.com/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title 1</h2> <p class="project__desc">Short Description Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequuntur, id?</p> </div> <div class="grid__item project"> <img src="https://www.fillmurray.com/g/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title 2</h2> <p class="project__desc">Short Description Lorem ipsum dolor sit amet.</p> </div> <div class="grid__item project"> <img src="https://www.fillmurray.com/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title 3</h2> <p class="project__desc">Short Description Lorem ipsum dolor sit amet consectetur, adipisicing elit. Aspernatur nostrum in obcaecati itaque explicabo voluptatibus corporis cumque praesentium eaque beatae!</p> </div> </div> <div class="split-loop__right"> <div class="grid__item project"> <img src="https://www.fillmurray.com/g/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title 4</h2> <p class="project__desc">Short Description Lorem, ipsum dolor sit amet consectetur adipisicing elit. Dolores nobis eius, minus optio ab earum.</p> </div> <div class="grid__item project"> <img src="https://www.fillmurray.com/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title 5</h2> <p class="project__desc">Short Description Lorem ipsum dolor sit amet consectetur adipisicing elit. Nihil?</p> </div> <div class="grid__item project"> <img src="https://www.fillmurray.com/g/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title 6</h2> <p class="project__desc">Short Description Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quia iste distinctio doloremque facere!</p> </div> </div> </div> </div>

Tres elementos por columna son muy pocos para cambiar suavemente en ventanas pequeñas. Necesitas jugar con shiftingThreshold .

over 4 years ago · Santiago Trujillo Denunciar

0

Este ejemplo es un poco descuidado y no usa jQuery, pero funciona con dos pergaminos interdependientes (bloques izquierdo y derecho). Espero que esto te ayude de alguna manera a resolver tu problema.

 const rootElement = document.querySelector(".split-loop"); const leftLoop = document.querySelector(".split-loop__left"); const rightLoop = document.querySelector(".split-loop__right"); const scrollHeight = leftLoop.scrollHeight; const offsetBoundary = 200; //the offset from the borders at which the element reordering event is triggered const lastScrollPos = new WeakMap(); const linkedLoops = new Map([ [leftLoop, rightLoop], [rightLoop, leftLoop], ]); let scrollLockElement = null; let scrollLockTimeout = null; // the function sets handlers to scrolling for infinite scrolling function infiniteScrollHandler(loop) { const virtualLoop = Array.from(loop.children); virtualLoop.forEach( (el) => (el.style.top = scrollHeight / 2 + el.offsetHeight + "px") ); loop.addEventListener("scroll", () => { if (virtualLoop.length < 2) return; // not enough items to scroll const topBound = loop.scrollTop; const bottomBound = loop.scrollTop + loop.offsetHeight; const firstEl = virtualLoop[0]; const lastEl = virtualLoop[virtualLoop.length - 1]; if (firstEl.offsetTop >= topBound - offsetBoundary) { lastEl.style.top = firstEl.offsetTop - lastEl.offsetHeight + "px"; virtualLoop.unshift(lastEl); virtualLoop.pop(); } else if ( lastEl.offsetTop + lastEl.offsetHeight < bottomBound + offsetBoundary ) { firstEl.style.top = lastEl.offsetTop + lastEl.offsetHeight + "px"; virtualLoop.push(firstEl); virtualLoop.shift(); } }); } // the function sets handlers to scrolling for reverse interaction with the linked loop function reverseLinkLoopHandler(loop) { loop.addEventListener("scroll", () => { const delta = lastScrollPos.get(loop) - loop.scrollTop; lastScrollPos.set(loop, loop.scrollTop); // this is blocked to prevent deadlock when events of two blocks are called each other. { if (scrollLockElement !== null && scrollLockElement !== loop) return; scrollLockElement = loop; clearTimeout(scrollLockTimeout); scrollLockTimeout = setTimeout( () => (scrollLockElement = null), 300 ); } linkedLoops .get(loop) .scrollTo(0, linkedLoops.get(loop).scrollTop + delta); }); } // set scroll handlers on all loops linkedLoops.forEach((loop) => { infiniteScrollHandler(loop); loop.scrollTo(0, scrollHeight / 2); lastScrollPos.set(loop, scrollHeight / 2); reverseLinkLoopHandler(loop); });
 /* Hide Scroll Bars */ ::-webkit-scrollbar { display: none; } html, body { margin: 0; padding: 0; -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ } /* Basic Styling */ img { border: 1px solid black; margin-bottom: 24px; width: 100%; max-width: 100%; } h2 { font-size: 14px; font-weight: normal; margin-bottom: 4px; font-family: "Inter", sans-serif; } p { color: black; font-size: 11px; font-family: "Inter", sans-serif; } /* Content will be in these eventually */ .bar-left, .bar-right { border-right: 1px solid black; box-sizing: border-box; height: 100vh; position: fixed; top: 0; left: 0; width: 48px; z-index: 10000; } .bar-right { border: none; border-left: 1px solid black; left: auto; right: 0; } /* Split Loop */ .split-loop { position: relative; margin: 0 48px; } .split-loop__left { width: 50%; overflow: auto; position: relative; max-height: 100vh; } .split-loop__right:before, .split-loop__left:before { display: block; content: ""; z-index: -1; height: 9999999px; } .split-loop__right { border-left: 1px solid black; box-sizing: border-box; position: fixed; right: 48px; bottom: 0; z-index: 5; width: calc(50% - 48px); overflow: auto; max-height: 100vh; } .project { box-sizing: border-box; border-bottom: 1px solid black; height: 600px; padding: 48px; position: absolute; } .project__media { /* height: 100px; */ }
 <header class="bar-left"></header> <div class="bar-right"></div> <div class="view"> <div class="grid split-loop" style="max-height: 100vh"> <div class="split-loop__left"> <div class="grid__item project"> <img src="https://www.fillmurray.com/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title</h2> <p class="project__desc">Short Description</p> </div> <div class="grid__item project"> <img src="https://www.fillmurray.com/g/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title</h2> <p class="project__desc">Short Description</p> </div> <div class="grid__item project"> <img src="https://www.fillmurray.com/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title</h2> <p class="project__desc">Short Description</p> </div> </div> <div class="split-loop__right"> <div class="grid__item project"> <img src="https://www.fillmurray.com/g/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title</h2> <p class="project__desc">Short Description</p> </div> <div class="grid__item project"> <img src="https://www.fillmurray.com/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title</h2> <p class="project__desc">Short Description</p> </div> <div class="grid__item project"> <img src="https://www.fillmurray.com/g/600/400" alt="" class="project__media" /> <h2 class="project__title">Project Title</h2> <p class="project__desc">Short Description</p> </div> </div> </div> </div>

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda