├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── public
├── effects
│ ├── Emotion_Meter.deepar
│ ├── Emotions_Exaggerator.deepar
│ ├── Fire_Effect.deepar
│ ├── Hope.deepar
│ ├── Humanoid.deepar
│ ├── MakeupLook.deepar
│ ├── Neon_Devil_Horns.deepar
│ ├── Ping_Pong.deepar
│ ├── Pixel_Hearts.deepar
│ ├── Snail.deepar
│ ├── Split_View_Look.deepar
│ ├── Stallone.deepar
│ ├── Vendetta_Mask.deepar
│ ├── flower_face.deepar
│ ├── galaxy_background_web.deepar
│ ├── ray-ban-wayfarer.deepar
│ └── viking_helmet.deepar
├── images
│ ├── bg-grid-dark.svg
│ ├── crystal.png
│ ├── gradient.svg
│ └── powered-by.svg
├── index.html
├── style.css
└── thumbs
│ ├── devil_horns.png
│ ├── fire.png
│ ├── flower_face.png
│ ├── galaxy.png
│ ├── hope.png
│ ├── humanoid.png
│ ├── makeup-split.png
│ ├── makeup.png
│ ├── ping_pong.png
│ ├── pixel_hearts.png
│ ├── ray-ban-wayfarer.png
│ ├── snail.png
│ ├── stallone.png
│ ├── vendetta.png
│ └── viking.png
├── src
├── carousel.js
└── index.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /dist
3 | package-lock.json
4 | .DS_Store
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 DeepAR SDK
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # quickstart-web-npm-js
2 |
3 | This is a quickstart project for DeepAR Web SDK. It is a minimal example of how to use the SDK.
4 |
5 | ## View the demo
6 |
7 | [https://demo.deepar.ai/quickstart-web-js-npm/](https://demo.deepar.ai/quickstart-web-js-npm/)
8 |
9 | ## How to run the project
10 |
11 | - Go to https://developer.deepar.ai
12 | - Sign up
13 | - Create a project and a Web app
14 | - Copy the license key
15 | - Paste it into `src/index.js` (replace `your_license_key_goes_here`)
16 | - Open the terminal in the root of the project
17 | - Run `npm install`
18 | - Run `npm run dev`
19 | - If the browser doesn't open automatically, open http://localhost:8888
20 |
21 | ## How to build the project
22 |
23 | - Open the terminal in the root of the project
24 | - Run `npm install`
25 | - Run `npm run build`
26 | - The build will be in the `dist` folder
27 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "quickstart-web-js-npm",
3 | "version": "1.0.0",
4 | "description": "Quickstart project for DeepAR Web.",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "rm -rf dist && webpack --config webpack.config.js --mode production && cp -r public/* dist && cp -r node_modules/deepar dist/deepar-resources",
8 | "dev": "webpack serve --mode development --open --port 8888"
9 | },
10 | "author": "",
11 | "license": "MIT",
12 | "devDependencies": {
13 | "webpack": "^5.74.0",
14 | "webpack-cli": "^4.10.0",
15 | "webpack-dev-server": "^4.11.0"
16 | },
17 | "dependencies": {
18 | "deepar": "^5.4.1"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/public/effects/Emotion_Meter.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/Emotion_Meter.deepar
--------------------------------------------------------------------------------
/public/effects/Emotions_Exaggerator.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/Emotions_Exaggerator.deepar
--------------------------------------------------------------------------------
/public/effects/Fire_Effect.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/Fire_Effect.deepar
--------------------------------------------------------------------------------
/public/effects/Hope.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/Hope.deepar
--------------------------------------------------------------------------------
/public/effects/Humanoid.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/Humanoid.deepar
--------------------------------------------------------------------------------
/public/effects/MakeupLook.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/MakeupLook.deepar
--------------------------------------------------------------------------------
/public/effects/Neon_Devil_Horns.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/Neon_Devil_Horns.deepar
--------------------------------------------------------------------------------
/public/effects/Ping_Pong.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/Ping_Pong.deepar
--------------------------------------------------------------------------------
/public/effects/Pixel_Hearts.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/Pixel_Hearts.deepar
--------------------------------------------------------------------------------
/public/effects/Snail.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/Snail.deepar
--------------------------------------------------------------------------------
/public/effects/Split_View_Look.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/Split_View_Look.deepar
--------------------------------------------------------------------------------
/public/effects/Stallone.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/Stallone.deepar
--------------------------------------------------------------------------------
/public/effects/Vendetta_Mask.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/Vendetta_Mask.deepar
--------------------------------------------------------------------------------
/public/effects/flower_face.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/flower_face.deepar
--------------------------------------------------------------------------------
/public/effects/galaxy_background_web.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/galaxy_background_web.deepar
--------------------------------------------------------------------------------
/public/effects/ray-ban-wayfarer.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/ray-ban-wayfarer.deepar
--------------------------------------------------------------------------------
/public/effects/viking_helmet.deepar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/effects/viking_helmet.deepar
--------------------------------------------------------------------------------
/public/images/bg-grid-dark.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/images/crystal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/images/crystal.png
--------------------------------------------------------------------------------
/public/images/gradient.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/images/powered-by.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 | DeepAR Web SDK - Face and body AR in the browser
11 |
12 |
13 |
14 |
15 |
16 |
17 |
25 |

31 |
43 |

48 |
49 |
50 |
51 |
52 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
84 |
85 |
86 |

87 |
88 |
89 |

90 |
91 |
92 |

93 |
94 |
95 |

96 |
97 |
98 |

99 |
100 |
101 |

102 |
103 |
104 |

105 |
106 |
107 |

108 |
109 |
110 |

111 |
112 |
113 |

114 |
115 |
116 |

117 |
118 |
119 |

120 |
121 |
122 |

123 |
124 |
125 |

126 |
127 |
128 |

129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/public/style.css:
--------------------------------------------------------------------------------
1 | canvas.deepar {
2 | border: 0px none;
3 | background-color: black;
4 | display: block;
5 | margin: auto;
6 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
7 | max-width: 100%;
8 | max-height: 100%;
9 | }
10 |
11 | body {
12 | margin: 0px;
13 | padding: 0px;
14 | font-family: sans-serif;
15 | }
16 |
17 | #loading-screen,
18 | #permission-denied-screen,
19 | #permission-denied-background {
20 | background-color: rgb(17 24 39 / 1);
21 | background-image: url("./images/gradient.svg"), url("./images/bg-grid-dark.svg");
22 | background-repeat: no-repeat, repeat;
23 | display: flex;
24 | align-items: center;
25 | justify-content: center;
26 | }
27 |
28 | .fixed-fullscreen {
29 | position: fixed;
30 | top: 0;
31 | left: 0;
32 | width: 100vw;
33 | height: 100vh;
34 | }
35 |
36 | /* Carousel */
37 |
38 | .carousel {
39 | position: absolute;
40 | bottom: 0;
41 | overflow: hidden;
42 | cursor: grab;
43 | display: flex;
44 | align-items: center;
45 | justify-content: center;
46 | height: 120px;
47 | width: 100%;
48 | }
49 |
50 | .carousel.active {
51 | cursor: grabbing;
52 | }
53 |
54 | .carousel-slider {
55 | position: absolute;
56 | display: flex;
57 | flex-direction: row;
58 | align-items: center;
59 | justify-content: center;
60 | width: 100%;
61 | top: 0;
62 | left: 0;
63 | width: fit-content;
64 | height: inherit;
65 | transition: left 0.3s ease;
66 | will-change: transform;
67 | }
68 |
69 | .carousel.active .inner {
70 | transition: none;
71 | }
72 |
73 | .carousel-center {
74 | width: 84px;
75 | height: 84px;
76 | border: 8px solid #fff;
77 | border-radius: 50%;
78 | z-index: 100;
79 | box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.5);
80 | -webkit-user-select: none;
81 | }
82 |
83 | .carousel-slider .slide {
84 | width: 70px;
85 | height: 70px;
86 | display: flex;
87 | align-items: center;
88 | justify-content: center;
89 | border-radius: 50%;
90 | margin-left: 10px;
91 | margin-right: 10px;
92 | }
93 |
94 | .carousel-slider .slide.active {
95 | margin-left: 24px;
96 | margin-right: 24px;
97 | width: 80px;
98 | height: 80px;
99 | }
100 |
101 | .slide {
102 | overflow: hidden;
103 | border: 2px solid white;
104 | -webkit-touch-callout: none; /* Safari Touch */
105 | -webkit-user-select: none; /* Webkit */
106 | -moz-user-select: none; /* Firefox */
107 | -ms-user-select: none; /* Edge*/
108 | user-select: none; /* Future-proof*/
109 | }
110 | .slide.active {
111 | background-color: #010c43;
112 | }
113 | .slide img {
114 | width: 100%;
115 | height: 100%;
116 | object-fit: cover;
117 | }
118 |
119 | /* Loading spinner */
120 |
121 | .lds-ring {
122 | display: inline-block;
123 | position: absolute;
124 | top: 50%;
125 | left: 50%;
126 | transform: translate(-50%, -50%);
127 | width: 80px;
128 | height: 80px;
129 | }
130 | .lds-ring div {
131 | box-sizing: border-box;
132 | display: block;
133 | position: absolute;
134 | width: 64px;
135 | height: 64px;
136 | margin: 8px;
137 | border: 8px solid #fff;
138 | border-radius: 50%;
139 | animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
140 | border-color: #fff transparent transparent transparent;
141 | }
142 | .lds-ring div:nth-child(1) {
143 | animation-delay: -0.45s;
144 | }
145 | .lds-ring div:nth-child(2) {
146 | animation-delay: -0.3s;
147 | }
148 | .lds-ring div:nth-child(3) {
149 | animation-delay: -0.15s;
150 | }
151 |
152 | @keyframes lds-ring {
153 | 0% {
154 | transform: rotate(0deg);
155 | }
156 | 100% {
157 | transform: rotate(360deg);
158 | }
159 | }
160 |
161 | /* Loading screen */
162 |
163 | @keyframes bounce {
164 | 0%,
165 | 100% {
166 | transform: translateY(-25%);
167 | animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
168 | }
169 | 50% {
170 | transform: none;
171 | animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
172 | }
173 | }
174 | .animate-bounce {
175 | animation: bounce 1s infinite;
176 | }
177 |
178 | #loading-progress-bar {
179 | width: 0%;
180 | background: rgb(0 98 209 / 1);
181 | height: 100%;
182 | transition: width 5s ease-out;
183 | }
184 |
185 | /* Permission denied screen */
186 |
187 | .permission-denied-text-container {
188 | position: absolute;
189 | left: 50%;
190 | top: 50%;
191 | transform: translateX(-50%) translateY(-50%);
192 | width: 100%;
193 | display: flex;
194 | align-items: center;
195 | justify-content: center;
196 | flex-direction: column;
197 | text-align: center;
198 | }
199 |
200 | .permission-denied-text {
201 | width: 400px;
202 | max-width: 95%;
203 | font-size: 16px;
204 | line-height: 1.2;
205 | color: #fff;
206 | }
207 |
208 | .permission-denied-button {
209 | margin-top: 20px;
210 | padding: 10px 20px;
211 | border-radius: 4px;
212 | background-color: #fff;
213 | color: #000;
214 | font-size: 16px;
215 | font-weight: 500;
216 | cursor: pointer;
217 | transition: background-color 0.2s ease;
218 | border: none;
219 | display: inline-block;
220 | text-decoration: none;
221 | width: fit-content;
222 |
223 | &:hover {
224 | background-color: #eaeaea;
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/public/thumbs/devil_horns.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/devil_horns.png
--------------------------------------------------------------------------------
/public/thumbs/fire.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/fire.png
--------------------------------------------------------------------------------
/public/thumbs/flower_face.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/flower_face.png
--------------------------------------------------------------------------------
/public/thumbs/galaxy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/galaxy.png
--------------------------------------------------------------------------------
/public/thumbs/hope.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/hope.png
--------------------------------------------------------------------------------
/public/thumbs/humanoid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/humanoid.png
--------------------------------------------------------------------------------
/public/thumbs/makeup-split.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/makeup-split.png
--------------------------------------------------------------------------------
/public/thumbs/makeup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/makeup.png
--------------------------------------------------------------------------------
/public/thumbs/ping_pong.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/ping_pong.png
--------------------------------------------------------------------------------
/public/thumbs/pixel_hearts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/pixel_hearts.png
--------------------------------------------------------------------------------
/public/thumbs/ray-ban-wayfarer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/ray-ban-wayfarer.png
--------------------------------------------------------------------------------
/public/thumbs/snail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/snail.png
--------------------------------------------------------------------------------
/public/thumbs/stallone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/stallone.png
--------------------------------------------------------------------------------
/public/thumbs/vendetta.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/vendetta.png
--------------------------------------------------------------------------------
/public/thumbs/viking.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepARSDK/quickstart-web-js-npm/ce85dcfac04c35f46a1c7d44e2df21ecbb049b6a/public/thumbs/viking.png
--------------------------------------------------------------------------------
/src/carousel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A draggable snap-like carousel, optimized to work smoothly across desktop and mobile devices.
3 | *
4 | */
5 |
6 | class Carousel {
7 | /**
8 | *
9 | * @param {String} sliderID the ID to query on the page
10 | * @param {String} sliderClass the className of the slider inner
11 | * @param {String} slideClass the className of the slider slides
12 | * @returns
13 | */
14 | constructor(
15 | sliderID,
16 | sliderClass = "carousel-slider",
17 | slideClass = "slide",
18 | centerID = "carousel-center"
19 | ) {
20 | // Grab the elements
21 | this.elem = document.getElementById(sliderID);
22 | if (this.elem == null) return Carousel.InvalidSliderStructure(sliderID);
23 |
24 | this.slider = this.elem.querySelector(`.${sliderClass}`);
25 | if (this.slider == null) return Carousel.InvalidSliderStructure(sliderID);
26 |
27 | this.slides = this.slider.querySelectorAll(`.${slideClass}`);
28 | if (!this.slides.length) return Carousel.InvalidSliderStructure(sliderID);
29 |
30 | this.center = document.getElementById(centerID);
31 | if (this.center == null) return Carousel.InvalidSliderStructure(sliderID);
32 |
33 | this.center.addEventListener("contextmenu", function (event) {
34 | event.preventDefault();
35 | });
36 |
37 | // this.slider.style.transition = "transform 0.3s ease";
38 |
39 | // Initialize properties
40 | this.activeSlide = null;
41 | this.startSlide = null;
42 | this.startX = null;
43 | this.dragStartX = null;
44 | this.moved = false;
45 | this.dragStarted = false;
46 | this.centerClick = false;
47 | this.activeHoldStarted = false;
48 | this.onChange = (activeSlide) => {};
49 | this.onActiveClick = () => {};
50 | this.onActiveHoldStart = () => {};
51 | this.onActiveHoldEnd = () => {};
52 |
53 | // Add event listeners
54 | this.elem.addEventListener("mousedown", (e) => {
55 | this.onDragStart(e);
56 | });
57 | this.elem.addEventListener("touchstart", (e) => {
58 | this.onDragStart(e);
59 | });
60 | this.elem.addEventListener("mouseleave", () => {
61 | if (this.moved) {
62 | this.onDragEnd();
63 | }
64 | });
65 | this.elem.addEventListener("mouseup", () => {
66 | if (this.activeHoldStarted) {
67 | this.endHoldTimer();
68 | } else if (this.moved) {
69 | this.onDragEnd();
70 | } else {
71 | // this.activeSlide = null;
72 | this.dragStarted = false;
73 | this.elem.classList.remove("active");
74 | this.slider.style.transition = "transform 0.3s ease";
75 |
76 | this.cancelHoldTimer();
77 |
78 | if (this.centerClick) {
79 | this.onActiveClick && this.onActiveClick();
80 | }
81 | this.centerClick = false;
82 | }
83 | });
84 | this.elem.addEventListener("touchend", () => {
85 | if (this.activeHoldStarted) {
86 | this.endHoldTimer();
87 | } else if (this.moved) {
88 | this.onDragEnd();
89 | } else {
90 | // this.activeSlide = null;
91 | this.dragStarted = false;
92 | this.elem.classList.remove("active");
93 | this.slider.style.transition = "transform 0.3s ease";
94 |
95 | this.cancelHoldTimer();
96 |
97 | // if (this.centerClick) {
98 | // this.onActiveClick && this.onActiveClick();
99 | // }
100 | this.centerClick = false;
101 | }
102 | });
103 | this.elem.addEventListener("mousemove", (e) => {
104 | if (this.dragStarted && !this.activeHoldStarted) {
105 | this.onMove(e);
106 | }
107 | });
108 | this.elem.addEventListener("touchmove", (e) => {
109 | if (this.dragStarted && !this.activeHoldStarted) {
110 | this.onMove(e);
111 | }
112 | });
113 | window.addEventListener("resize", () => {
114 | this.moveToActiveSlide();
115 | });
116 |
117 | // add event listener to all slides to track clicks
118 | this.slides.forEach((slide, index) => {
119 | slide.addEventListener("mouseup", (e) => {
120 | if (!this.moved) {
121 | this.elem.classList.remove("active");
122 | this.setActiveSlide(index);
123 | this.moveToActiveSlide();
124 | this.moved = false;
125 | this.dragStarted = false;
126 | this.centerClick = false;
127 | }
128 | });
129 | });
130 |
131 | // Set the active slide and classes
132 | this.setActiveSlide(0);
133 | this.moveToActiveSlide();
134 | }
135 |
136 | /**
137 | * Logs a warning for an invalid slider structure (empty or missing elements)
138 | *
139 | * @param {String} sliderID
140 | */
141 | static InvalidSliderStructure = (sliderID) => {
142 | console.warn(
143 | `The DraggableSlider with ID ${sliderID} will not work as it has some missing elements.`
144 | );
145 | };
146 |
147 | /**
148 | * Get the index of the slide which is closest to the center of the slider
149 | *
150 | * @returns {Number} the index of the slide
151 | */
152 | getActiveSlide = () => {
153 | let res = 0;
154 | for (let i = 1; i < this.slides.length; i++)
155 | if (
156 | Math.abs(this.getSlideOffset(i)) < Math.abs(this.getSlideOffset(i - 1))
157 | )
158 | res = i;
159 | return res;
160 | };
161 |
162 | /**
163 | * Get the absolute horizontal offset of a slide from the slider's center point
164 | *
165 | * @param {Number} slide the index of the slide
166 | * @returns {Number} the absolute of the offset in pixels
167 | */
168 | getSlideOffset = (slide) => {
169 | let sliderRect = this.elem.getBoundingClientRect();
170 | let slideRect = this.slides[slide].getBoundingClientRect();
171 | return (
172 | slideRect.left +
173 | slideRect.width / 2 -
174 | (sliderRect.left + sliderRect.width / 2)
175 | );
176 | };
177 |
178 | /**
179 | * Get the left CSS property value of the slider's inner
180 | * @returns {Number}
181 | */
182 | getInnerLeft = () => {
183 | let res = parseInt(this.slider.style.transform.split("(")[1]);
184 | if (isNaN(res) || res == null) return 0;
185 | return res;
186 | };
187 |
188 | /**
189 | * Initialize the active state of the slider
190 | *
191 | * @param {Event} e
192 | */
193 | onDragStart = (e) => {
194 | console.log("drag start");
195 | const centerClick = e.target.id === "carousel-center";
196 |
197 | this.slider.style.transition = "";
198 |
199 | if (centerClick) {
200 | this.centerClick = true;
201 | }
202 |
203 | this.elem.classList.add("active");
204 | this.moved = false;
205 | this.startX = e.pageX || e.touches[0].pageX;
206 | this.dragStartX = e.pageX || e.touches[0].pageX;
207 | this.startSlide = this.activeSlide;
208 | this.dragStarted = true;
209 |
210 | this.startHoldTimer();
211 | };
212 |
213 | /**
214 | * Remove the active state of the slider
215 | */
216 | onDragEnd = () => {
217 | console.log("drag end");
218 | this.elem.classList.remove("active");
219 | this.slider.style.transition = "transform 0.3s ease";
220 |
221 | this.cancelHoldTimer();
222 |
223 | this.moveToActiveSlide();
224 | this.moved = false;
225 | this.dragStarted = false;
226 | this.dragStartX = null;
227 | this.centerClick = false;
228 | };
229 |
230 | /**
231 | * Get the touch / mouse move distance and move the slider inner
232 | *
233 | * @param {Event} e
234 | */
235 | onMove = (e) => {
236 | let pos = e.pageX || e.touches?.[0].pageX;
237 |
238 | // console.log("on move");
239 | // console.log("start of drag:", this.startX);
240 | // console.log("new position:", e.pageX);
241 | // console.log("new position (touch):", e.touches?.[0].pageX);
242 | // console.log("pos:", pos);
243 |
244 | const movedDistance = pos - this.dragStartX;
245 | const moved = movedDistance >= 1 || movedDistance <= -1;
246 |
247 | if (moved) {
248 | this.moved = true;
249 | if (this.holdTimer) {
250 | this.cancelHoldTimer();
251 | }
252 | }
253 |
254 | if (!this.elem.classList.contains("active")) return;
255 |
256 | e.preventDefault();
257 |
258 | let dist = pos - this.startX;
259 |
260 | this.startX = pos;
261 | // this.slider.style.left = this.getInnerLeft() + dist + "px";
262 | this.slider.style.transform = `translate(${this.getInnerLeft() + dist}px)`;
263 | this.setActiveSlide(this.getActiveSlide());
264 | this.centerClick = false;
265 | };
266 |
267 | /**
268 | * Move the slider to the active slide
269 | */
270 | moveToActiveSlide = () => {
271 | if (this.activeSlide == null) this.activeSlide = 0;
272 | this.moveToSlide(this.activeSlide);
273 | };
274 |
275 | /**
276 | * Move the slider to a specified slide, to the last one if the number is greater than the
277 | * amount of slides or to the first one if the number is inferior to zero.
278 | *
279 | * @param {Number} slide the index of the slide
280 | */
281 | moveToSlide = (slide) => {
282 | this.slider.style.transform = `translate(${
283 | this.getInnerLeft() - this.getSlideOffset(this.activeSlide) + "px"
284 | })`;
285 |
286 | setTimeout(() => {
287 | this.onChange && this.onChange(this.activeSlide);
288 | }, 100);
289 | };
290 |
291 | /**
292 | * Set a slide as active and update the active class
293 | *
294 | * @param {Number} slide the slide to set as active
295 | */
296 | setActiveSlide = (slide) => {
297 | // Edge cases
298 | if (slide < 0) slide = 0;
299 | if (slide > this.slides.length - 1) slide = this.slides.length - 1;
300 |
301 | this.activeSlide = slide;
302 | for (let i = 0; i < this.slides.length; i++) {
303 | if (i == this.activeSlide) this.slides[i].classList.add("active");
304 | else this.slides[i].classList.remove("active");
305 | }
306 | };
307 |
308 | startHoldTimer = () => {
309 | console.log("startHoldTimer");
310 | this.holdTimer = setTimeout(() => {
311 | console.log("this.moved", this.moved);
312 | if (!this.moved) {
313 | this.onActiveHoldStart && this.onActiveHoldStart();
314 | this.activeHoldStarted = true;
315 | }
316 | this.activeHoldStarted = true;
317 | this.holdTimer = null;
318 | }, 500); // Customize the delay before 'onActiveHoldStart' event fires
319 | };
320 |
321 | endHoldTimer = () => {
322 | console.log("endHoldTimer");
323 | clearTimeout(this.holdTimer);
324 | this.activeHoldStarted = false;
325 | if (this.onActiveHoldEnd) {
326 | this.onActiveHoldEnd();
327 | }
328 | };
329 |
330 | cancelHoldTimer = () => {
331 | console.log("cancelHoldTimer");
332 | clearTimeout(this.holdTimer);
333 | };
334 | }
335 |
336 | export default Carousel;
337 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import * as deepar from "deepar";
2 | import Carousel from "./carousel.js";
3 |
4 | // Log the version. Just in case.
5 | console.log("Deepar version: " + deepar.version);
6 |
7 | // Top-level await is not supported.
8 | // So we wrap the whole code in an async function that is called immediatly.
9 | (async function () {
10 | // Get the element you want to place DeepAR into. DeepAR will inherit its width and height from this and fill it.
11 | const previewElement = document.getElementById("ar-screen");
12 |
13 | // trigger loading progress bar animation
14 | const loadingProgressBar = document.getElementById("loading-progress-bar");
15 | loadingProgressBar.style.width = "100%";
16 |
17 | // All the effects are in the public/effects folder.
18 | // Here we define the order of effect files.
19 | const effectList = [
20 | "effects/ray-ban-wayfarer.deepar",
21 | "effects/viking_helmet.deepar",
22 | "effects/MakeupLook.deepar",
23 | "effects/Split_View_Look.deepar",
24 | "effects/flower_face.deepar",
25 | "effects/Stallone.deepar",
26 | "effects/galaxy_background_web.deepar",
27 | "effects/Humanoid.deepar",
28 | "effects/Neon_Devil_Horns.deepar",
29 | "effects/Ping_Pong.deepar",
30 | "effects/Pixel_Hearts.deepar",
31 | "effects/Snail.deepar",
32 | "effects/Hope.deepar",
33 | "effects/Vendetta_Mask.deepar",
34 | "effects/Fire_Effect.deepar",
35 | ];
36 |
37 | let deepAR = null;
38 |
39 | // Initialize DeepAR with an effect file.
40 | try {
41 | deepAR = await deepar.initialize({
42 | licenseKey: "your_license_key_goes_here",
43 | previewElement,
44 | effect: effectList[0],
45 | // Removing the rootPath option will make DeepAR load the resources from the JSdelivr CDN,
46 | // which is fine for development but is not recommended for production since it's not optimized for performance and can be unstable.
47 | // More info here: https://docs.deepar.ai/deepar-sdk/platforms/web/tutorials/download-optimizations/#custom-deployment-of-deepar-web-resources
48 | rootPath: "./deepar-resources",
49 | additionalOptions: {
50 | cameraConfig: {
51 | // facingMode: 'environment' // uncomment this line to use the rear camera
52 | },
53 | },
54 | });
55 | } catch (error) {
56 | console.error(error);
57 | document.getElementById("loading-screen").style.display = "none";
58 | document.getElementById("permission-denied-screen").style.display = "block";
59 | return;
60 | }
61 |
62 | // Hide the loading screen.
63 | document.getElementById("loading-screen").style.display = "none";
64 | document.getElementById("ar-screen").style.display = "block";
65 |
66 | window.effect = effectList[0];
67 |
68 | const glassesCarousel = new Carousel("carousel");
69 | glassesCarousel.onChange = async (value) => {
70 | const loadingSpinner = document.getElementById("loading-spinner");
71 |
72 | if (window.effect !== effectList[value]) {
73 | loadingSpinner.style.display = "block";
74 | await deepAR.switchEffect(effectList[value]);
75 | window.effect = effectList[value];
76 | }
77 | loadingSpinner.style.display = "none";
78 | };
79 | })();
80 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | entry: "./src/index.js",
5 | output: {
6 | filename: "main.js",
7 | path: path.resolve(__dirname, "dist"),
8 | clean: true,
9 | },
10 | target: "web",
11 | module: {
12 | rules: [
13 | {
14 | test: /\.(wasm)|(bin)|(obj)$/i,
15 | include: [
16 | path.resolve(__dirname, 'node_modules/deepar/'),
17 | ],
18 | type: 'asset/resource',
19 | },
20 | {
21 | include: [
22 | path.resolve(__dirname, 'effects/'),
23 | ],
24 | type: 'asset/resource',
25 | },
26 | ],
27 | },
28 | resolve: {
29 | alias: {
30 | '@effects': path.resolve(__dirname, 'effects/'),
31 | },
32 | },
33 | performance: {
34 | maxEntrypointSize: 1000000,
35 | maxAssetSize: 10000000,
36 | },
37 | devServer: {
38 | static: [
39 | {
40 | directory: path.join(__dirname, "public"),
41 | },
42 | {
43 | directory: path.join(__dirname, "node_modules/deepar"),
44 | publicPath: "/deepar-resources",
45 | },
46 | ],
47 | compress: true,
48 | port: 9000,
49 | },
50 | };
51 |
--------------------------------------------------------------------------------