Skip to content

Commit

Permalink
feat: carousel
Browse files Browse the repository at this point in the history
  • Loading branch information
mohamadHarith committed Jun 15, 2021
1 parent c891d90 commit eb2ed81
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 90 deletions.
204 changes: 123 additions & 81 deletions components/carousel/src/Carousel.svelte
Original file line number Diff line number Diff line change
@@ -1,114 +1,156 @@
<script lang="ts">
import { onMount } from "svelte";
import { getNodeAttribute } from "@wetix/utils";
// import type { CarouselItem } from "../types/index";
import type { CarouselItem } from "../types/index";
let className = "";
export { className as class };
export let infinite = false;
export let padding = 10;
export let duration = 500;
export let perPage = 1;
export let heightRatio = 0.56; // slideHeight/ slideWidth
export let gap = 10; // gap between the slides
export let perPage = 1; // slides per page
export let leftAndRight = 30; // left and right slide peek
export let style = "";
export let items: CarouselItem[] = [];
export let autoPlay = false;
let el: HTMLDivElement;
let width = 0;
let slideWidth = 0;
let slideHeight = 0;
let slideStyle = "";
let translateX = 0;
const addClones = () => {
const first = el.children[0];
const last = el.children[el.children.length - 1];
el.prepend(last.cloneNode(true));
el.append(first.cloneNode(true));
// add clone to front and back
if (items.length > 0) {
items.push(items[0]);
items.unshift(items[items.length - 1]);
}
const onRight = () => {
const right = Math.floor(slideWidth + gap);
el.scrollBy(right, 0);
const rightScrolled = el.scrollLeft;
const carouselTotalWidth =
slideWidth * items.length + gap * (items.length - 1) - translateX;
if (rightScrolled >= carouselTotalWidth - width - translateX) {
el.scrollBy(-el.scrollLeft, 0);
}
};
function applyPageSizes() {
const children = el.children as HTMLCollectionOf<HTMLElement>;
const slideWidth = window.innerWidth - padding * 2;
// pageWidth = pageWindowElement.clientWidth;
width = slideWidth * children.length + 2;
for (let i = 0; i < children.length; i++) {
children[i].className = "responsive-ui-carousel__slide";
children[i].style.width = `${slideWidth}px`;
// children[i].style.maxWidth = `${slideWidth}px`;
const onLeft = () => {
if (el.scrollLeft === 0) {
const carouselTotalWidth =
slideWidth * items.length + gap * (items.length - 1) - translateX;
el.scrollBy(carouselTotalWidth - width - translateX, 0);
}
// store.init(initialPageIndex + Number(infinite));
// offsetPage(false);
const left = Math.floor(slideWidth + gap);
el.scrollBy(-left, 0);
};
if (autoPlay) {
setInterval(onRight, 3000);
}
const getTouches = (e: any) => {
if (e.changedTouches != undefined) {
e = e.changedTouches[0];
const handleClick = (e: Event) => {
const data = getNodeAttribute(e, "data-value");
if (data) {
window.location.href = data;
}
const { clientX, clientY } = e;
return { x: clientX, y: clientY };
};
let offset = 0;
onMount(() => {
addClones();
applyPageSizes();
// const childNodes = Array.from(el.childNodes);
// const slideWidth = (window.innerWidth * 1) / 1;
// const fragment = document.createDocumentFragment();
// childNodes.forEach((v) => {
// const clone = v.cloneNode(true);
// fragment.appendChild(clone);
// });
// el.replaceWith(fragment);
// console.log(childNodes);
let x = 0;
let y = 0;
el.addEventListener("touchstart", (e) => {
const evt = getTouches(e);
x = evt.x;
y = evt.y;
});
el.addEventListener("touchmove", (e) => {
e.preventDefault();
const evt = getTouches(e);
console.log(evt);
});
el.addEventListener("touchend", console.log);
});
$: {
if (el) {
slideWidth = (width - ((perPage + 1) * gap + 2 * leftAndRight)) / perPage;
slideHeight = heightRatio * slideWidth;
translateX = slideWidth - leftAndRight;
slideStyle = `--slideWidth:${slideWidth}px;--slideHeight:${slideHeight}px;--slideMarginRight:${gap}px;--tx:-${translateX}px;}`;
}
}
</script>

<div
style={`${style}`}
bind:this={el}
draggable="true"
class="{className} responsive-ui-carousel"
style={`width: ${width}px; transform: translateX(${offset}px); transition: transform ${duration}ms;${style}`}
bind:offsetWidth={width}
class="responsive-ui-carousel"
on:mousedown|preventDefault
on:touchmove|preventDefault
on:mousewheel|preventDefault
on:click={handleClick}
>
<slot />
<!-- {#each items as item, i}
<div class="responsive-ui-carousel__slide" style="width:{width}px">
{item}
<div
class="left-btn"
style={`margin-top:${0.5 * slideHeight}px`}
on:click|stopPropagation={onLeft}
>
&lt
</div>
<div
class="right-btn"
style={`margin-top:${0.5 * slideHeight}px; margin-left:${width - 30}px;`}
on:click|stopPropagation={onRight}
>
&gt
</div>
{#each items as item}
<div
class="responsive-ui-carousel__slide"
data-value={item.url}
style={slideStyle}
>
<img src={item.src} alt="" />
</div>
{/each} -->
<!-- <div class="slide-content">Slide 1</div>
<div class="slide-content">Slide 2</div>
<div class="slide-content">Slide 3</div>
<div class="slide-content">Slide 4</div> -->
{/each}
</div>

<style lang="scss">
.responsive-ui-carousel {
box-sizing: border-box;
position: relative;
width: 100%;
// display: flex; /* to put child elements in one row */
transition-property: transform;
overflow: scroll;
white-space: nowrap;
// flex-wrap: nowrap;
display: inline-flex;
overflow-x: scroll;
scroll-behavior: smooth;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
.left-btn,
.right-btn {
position: fixed;
cursor: pointer;
z-index: 1;
width: 30px;
height: 25px;
background-color: rgba(0, 0, 0, 0.3);
color: white;
}
.responsive-ui-carousel__slide {
cursor: pointer;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);
background-color: lightgray;
min-width: var(--slideWidth);
max-width: var(--slideWidth);
min-height: var(--slideHeight);
max-height: var(--slideHeight);
margin-right: var(--slideMarginRight);
transform: translateX(var(--tx));
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
}
:global(.responsive-ui-carousel__slide) {
display: inline-block;
vertical-align: middle;
height: 100%;
border-radius: 5px;
border: 1px solid red;
/* Hide scrollbar for Chrome, Safari and Opera */
.responsive-ui-carousel::-webkit-scrollbar {
display: none;
}
</style>
5 changes: 4 additions & 1 deletion components/carousel/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { SvelteComponentTyped } from "svelte/internal";

export type CarouselItem = {};
export type CarouselItem = {
src:string;
url:string;
};

export interface CarouselProps {
class?: string;
Expand Down
55 changes: 47 additions & 8 deletions src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -435,16 +435,55 @@
let placement = "left";
let innerWidth = window.innerWidth;
</script>

<AppBar title="XXXX" hasBg={true} />
<Carousel items={[1, 2, 3, 4, 5]} style="margin-top:20px;">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</Carousel>
<svelte:window bind:innerWidth />

<AppBar title={`xxx`} hasBg={true} />

<div style={`width:100%; display:flex; justify-content:center;`}>
<Carousel
items={[
{
src: "https://asset.wetix.my/images/1tOzB9WuHEsqNXEAMRj7Hzqn8q0.jpg",
url: "https://google.com",
},
{
src: "https://asset.wetix.my/images/marathon.png",
url: "https://google.com",
},
{
src:
"https://asset.wetix.my/images/af910e1d-9112-4fec-b4b5-3671c1b4c87b.jpg",
url: "https://google.com",
},
{
src:
"https://asset.wetix.my/images/39131d05-37d9-4902-948b-183e24a8a2ae.jpeg",
url: "https://google.com",
},
{
src:
"https://asset.wetix.my/images/da132468-8677-4675-b8f5-c32f9a751f3a.png",
url: "https://google.com",
},
{
src:
"https://asset.wetix.my/images/02a86a0b-ac84-4df1-ba71-d707045815e6.png",
url: "https://google.com",
},
]}
heightRatio={0.55}
gap={10}
leftAndRight={innerWidth > 640 ? 30 : 20}
perPage={innerWidth > 640 ? 3 : 1}
style="width:100%;"
autoPlay={false}
/>
</div>

<main>
<Badge count={1}>testing</Badge>
<Badge count={0}>testing</Badge>
Expand Down

0 comments on commit eb2ed81

Please sign in to comment.