ai tools section videos for live portrait and video style transfer (#656)

* vst video block added

* vst video and poster update

* ai tools live portrait video

* touch start for mobile and ai tools page update
This commit is contained in:
BFlat authored and GitHub committed 2024-08-08 05:48:04 +07:00
1 parent a0dac5ec29
commit c5cb07d391
14 files changed
+162 -30

No files matched your search

+1 -1
View File
@@ -77,7 +77,7 @@
/terms* /website/index.html 200
/testing* /website/index.html 200
/text-to-image* /website/index.html 200
/tools* /website/index.html 200
/ai-tools* /website/index.html 200
/tts* /website/index.html 200
/tts_configs* /website/index.html 200
/twitch* /website/index.html 200
@@ -77,7 +77,7 @@
/terms* /website/index.html 200
/testing* /website/index.html 200
/text-to-image* /website/index.html 200
/tools* /website/index.html 200
/ai-tools* /website/index.html 200
/tts* /website/index.html 200
/tts_configs* /website/index.html 200
/twitch* /website/index.html 200
+1 -1
View File
@@ -53,7 +53,7 @@
/terms* /index.html 200
/testing* /index.html 200
/text-to-image* /index.html 200
/tools* /index.html 200
/ai-tools* /index.html 200
/tts* /index.html 200
/tts_configs* /index.html 200
/twitch* /index.html 200
Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

@@ -458,7 +458,7 @@ export default function TopNav({
<NavItem
icon={faScrewdriverWrench}
label="Creator Tools"
link="/tools"
link="/ai-tools"
/>
</div>
)}
@@ -605,7 +605,7 @@ export default function TopNav({
<li>
<NavLink
exact={true}
to="/tools"
to="/ai-tools"
activeClassName="active-link"
onClick={handleNavLinkClick}
>
@@ -40,7 +40,8 @@ export default function AITools() {
to: "/style-video",
title: t("productVideoStyleTransferTitle"),
text: t("productVideoStyleTransferText"),
imgSrc: "/images/landing/select-vst.webp",
videoSrc: "/videos/ai-tools/vst_video.mp4",
videoPosterSrc: "/images/ai-tools/vst_video_poster.jpg",
imgAlt: "Video Style Transfer",
badgeContent: {
type: "new",
@@ -52,7 +53,8 @@ export default function AITools() {
to: "/ai-face-mirror",
title: t("productLivePortraitTitle"),
text: t("productLivePortraitText"),
imgSrc: "/images/landing/select-fm.webp",
videoSrc: "/videos/ai-tools/lp_video.mp4",
videoPosterSrc: "/images/ai-tools/lp_video_poster.jpg",
imgAlt: "Live Portrait",
badgeContent: {
type: "new",
@@ -1,8 +1,8 @@
import React from "react";
import React, { useEffect, useRef, useState } from "react";
import { Link } from "react-router-dom";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import { faArrowRight } from "@fortawesome/pro-solid-svg-icons";
import { faLongArrowRight } from "@fortawesome/pro-solid-svg-icons";
interface BadgeContent {
type: string;
@@ -14,9 +14,11 @@ interface AIToolsItemProps {
to: string;
title: string;
text?: string;
imgSrc: string;
imgSrc?: string;
imgAlt: string;
badgeContent?: BadgeContent;
videoSrc?: string;
videoPosterSrc?: string;
}
export function AIToolsItem({
@@ -26,14 +28,69 @@ export function AIToolsItem({
imgSrc,
imgAlt,
badgeContent,
videoSrc,
videoPosterSrc,
}: AIToolsItemProps) {
const [isPlaying, setIsPlaying] = useState(false);
const [showPoster, setShowPoster] = useState(true);
const videoRef = useRef<HTMLVideoElement>(null);
const handleMouseEnter = () => {
if (videoRef.current && !isPlaying) {
setShowPoster(false);
videoRef.current.play();
setIsPlaying(true);
}
};
const handleTouchStart = () => {
if (videoRef.current && !isPlaying) {
setShowPoster(false);
videoRef.current.play();
setIsPlaying(true);
}
};
const handleVideoEnded = () => {
if (videoRef.current) {
videoRef.current.pause();
setShowPoster(true);
setTimeout(() => {
if (videoRef.current) {
videoRef.current.currentTime = 0;
}
setIsPlaying(false);
}, 200);
}
};
const handleTimeUpdate = () => {
if (videoRef.current) {
const timeRemaining =
videoRef.current.duration - videoRef.current.currentTime;
if (timeRemaining <= 0.2) {
setShowPoster(true);
}
}
};
useEffect(() => {
const videoElement = videoRef.current;
if (videoElement) {
videoElement.addEventListener("timeupdate", handleTimeUpdate);
return () => {
videoElement.removeEventListener("timeupdate", handleTimeUpdate);
};
}
}, []);
return (
<div className="col-12 col-md-6 col-lg-4">
<Link
to={to}
className="panel panel-select d-flex flex-column align-items-center"
>
<div className="d-flex px-4 pt-4 align-items-start w-100">
<div className="d-flex px-3 pt-3 px-xl-4 pt-xl-4 align-items-start w-100">
<div className="flex-grow-1">
{badgeContent && (
<div className="mb-1">
@@ -57,10 +114,63 @@ export function AIToolsItem({
)}
</div>
<Link to={to} className="btn btn-square mt-1">
<FontAwesomeIcon icon={faArrowRight} />
<FontAwesomeIcon
icon={faLongArrowRight}
className="btn-icon fs-5"
/>
</Link>
</div>
<img className="img-fluid" src={imgSrc} alt={imgAlt} />
{imgSrc && <img className="img-fluid" src={imgSrc} alt={imgAlt} />}
{videoSrc && (
<div
className="w-100 mt-3 px-3 px-xl-4 overflow-hidden"
onMouseEnter={handleMouseEnter}
onTouchStart={handleTouchStart}
>
<div
className="w-100 h-100 position-relative overflow-hidden"
style={{
borderTopLeftRadius: "0.5rem",
borderTopRightRadius: "0.5rem",
}}
>
{videoPosterSrc && (
<div
className={`h-100 w-100 ${
showPoster ? "opacity-100" : "opacity-0"
}`}
style={{
position: "absolute",
top: 0,
left: 0,
opacity: 0,
transition: "opacity 0.2s ease-in-out",
}}
>
<img
src={videoPosterSrc}
className="h-100 w-100 object-fit-cover"
alt={imgAlt}
/>
</div>
)}
<video
ref={videoRef}
muted={true}
playsInline={true}
className="w-100 h-100 object-fit-cover"
style={{
borderTopLeftRadius: "0.5rem",
borderTopRightRadius: "0.5rem",
}}
onEnded={handleVideoEnded}
>
<source src={videoSrc} type="video/mp4" />
</video>
</div>
</div>
)}
</Link>
</div>
);
@@ -1567,7 +1567,7 @@ styles with important tags:
transition: 0.2s all;
border-radius: 0.5rem !important;
border-width: 2px !important;
height: 320px;
height: 330px;
overflow: hidden;
z-index: 2;
@@ -1590,6 +1590,18 @@ styles with important tags:
transition: 0.2s all;
background-color: lighten($primary, 5%);
}
.btn-icon {
transition: 0.2s all;
}
&:hover .btn .btn-icon {
transform: translateX(3px);
}
@media (max-width: 768px) {
height: 280px;
}
}
.fs-14 {
@@ -990,7 +990,7 @@ class PageContainer extends React.Component<
<BetaKeysListPage />
</Route>
<Route path="/tools">
<Route path="/ai-tools">
<CreatorToolsPage
sessionWrapper={this.props.sessionWrapper}
/>
@@ -94,7 +94,7 @@ function FooterNav(props: Props) {
},
{ link: "/explore", text: "Explore Videos", icon: null },
...(domain.titlePart === "FakeYou"
? [{ link: "/tools", text: "AI Tools", icon: null }]
? [{ link: "/ai-tools", text: "AI Tools", icon: null }]
: []),
// { link: "/upload-assets", text: "Upload Assets", icon: null },
],
@@ -4,7 +4,6 @@ import {
faScrewdriverWrench,
faSparkles,
} from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import {
GetWebsite,
@@ -17,6 +16,7 @@ import { useLocalize } from "hooks";
import React from "react";
import { AIToolsRow } from "components/marketing";
import "./CreatorTools.scss";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
interface CreatorToolsPageProps {
sessionWrapper: SessionWrapper;
@@ -28,13 +28,6 @@ export default function CreatorToolsPage(props: CreatorToolsPageProps) {
usePrefixedDocumentTitle("Creator Tools");
const videoProducts = [
{
to: "/face-animator",
title: t("productLipsyncTitle"),
text: t("productLipsyncText"),
imgSrc: "/images/landing/select-fa.webp",
imgAlt: "Lipsync",
},
// {
// to: "/text-to-image",
// title: t("productImageGenTitle"),
@@ -51,7 +44,8 @@ export default function CreatorToolsPage(props: CreatorToolsPageProps) {
to: "/style-video",
title: t("productVideoStyleTransferTitle"),
text: t("productVideoStyleTransferText"),
imgSrc: "/images/landing/select-vst.webp",
videoSrc: "/videos/ai-tools/vst_video.mp4",
videoPosterSrc: "/images/ai-tools/vst_video_poster.jpg",
imgAlt: "Video Style Transfer",
badgeContent: {
type: "new",
@@ -64,7 +58,8 @@ export default function CreatorToolsPage(props: CreatorToolsPageProps) {
to: "/ai-face-mirror",
title: t("productLivePortraitTitle"),
text: t("productLivePortraitText"),
imgSrc: "/images/landing/select-fm.webp",
videoSrc: "/videos/ai-tools/lp_video.mp4",
videoPosterSrc: "/images/ai-tools/lp_video_poster.jpg",
imgAlt: "Live Portrait",
badgeContent: {
type: "new",
@@ -72,6 +67,14 @@ export default function CreatorToolsPage(props: CreatorToolsPageProps) {
label: "NEW",
},
},
{
to: "/face-animator",
title: t("productLipsyncTitle"),
text: t("productLipsyncText"),
imgSrc: "/images/landing/select-fa.webp",
imgAlt: "Lipsync",
},
];
const voiceProducts = [
@@ -111,10 +114,15 @@ export default function CreatorToolsPage(props: CreatorToolsPageProps) {
return (
<Container type="panel">
<Panel clear={true} className="mt-5">
<h1 className="fw-bold mb-4">
<FontAwesomeIcon icon={faScrewdriverWrench} className="me-3" />
Creator Tools
</h1>
<div className="mb-4">
<h1 className="fw-bold mb-1 d-flex align-items-center">
<FontAwesomeIcon icon={faScrewdriverWrench} className="me-3 fs-2" />
Creator Tools
</h1>
<h5 className="opacity-75">
AI-powered tools for video and voice creation.
</h5>
</div>
<div className="d-flex flex-column gap-5">
<div>