Compare commits

...

3 Commits

Author SHA1 Message Date
al e9e95f3749 编辑图片拖拽 2024-11-06 15:54:13 +08:00
al de9e8e747e 优化代码 2024-11-06 14:19:26 +08:00
al e01fbaa9af 修复上传视频自动播放问题 2024-11-06 11:52:28 +08:00
10 changed files with 208 additions and 159 deletions

View File

@ -299,7 +299,6 @@ function Login({ handleLogin }) {
<OwnInput
clearable={true}
placeholder="请输入手机号"
// disabled={true}
name="phone_number"
type="number"
maxLength={11}
@ -330,7 +329,6 @@ function Login({ handleLogin }) {
/> */}
<OwnInput
placeholder="请输入验证码"
// disabled={true}
name="veriCode"
type="number"
onChange={setVeriCode}
@ -382,7 +380,6 @@ function Login({ handleLogin }) {
<OwnInput
clearable={true}
placeholder="请输入手机号"
// disabled={true}
name="phone_number"
type="number"
maxLength={11}
@ -417,7 +414,6 @@ function Login({ handleLogin }) {
<OwnInput
clearable={true}
placeholder="请输入密码"
// disabled={true}
name="password"
type="password"
onChange={(value) => {

View File

@ -316,7 +316,6 @@ export default function CreateProfile() {
//
// superfan_price_list,
};
debugger;
const _data = await requireAPI(
"POST",
"/api/zone/create",

View File

@ -36,6 +36,7 @@ import requireAPI from "@/utils/requireAPI";
import { get } from "@/utils/storeInfo";
import UploadImgs from "@/components/UploadImgs";
import { multiUploadImage } from "@/utils/upload";
import { getVideoBase64 } from "@/utils/tools";
const constellations = [
{ label: "白羊座", value: "白羊座" },
{ label: "金牛座", value: "金牛座" },
@ -480,41 +481,19 @@ export default function CompleteStreamerInformation() {
],
}));
} else {
creatVideoCanvas(file);
setFormData((old) => ({
...old,
displayVideo: [
{
url: frameImageUrl,
const url = URL.createObjectURL(file);
getVideoBase64(url).then((src) => {
setFrameImageUrl(src);
setFormData((old) => ({
...old,
displayVideo: {
url: src,
file: Array.from(e.target.files),
},
],
}));
}));
});
}
};
const creatVideoCanvas = (file) => {
if (typeof window == "undefined") return;
const videoD = document.getElementById("video_complete");
const url = URL.createObjectURL(file);
videoD.src = url;
videoD.addEventListener("loadeddata", function () {
videoD.pause();
videoD.currentTime = 1;
});
videoD.addEventListener("seeked", function () {
const canvas = document.createElement("canvas");
canvas.width = videoD.videoWidth;
canvas.height = videoD.videoHeight;
canvas
.getContext("2d")
.drawImage(videoD, 0, 0, canvas.width, canvas.height);
const canvasImg = canvas.toDataURL();
setFrameImageUrl(canvasImg);
// URL
URL.revokeObjectURL(url);
});
};
//
if (isWaitingReview) {
return (
@ -1131,12 +1110,6 @@ export default function CompleteStreamerInformation() {
<SpinLoading color="default" />
</div>
</Mask>
<div className="hidden">
<video id="video_complete" controls autoPlay name="media">
<source />
您的浏览器不支持 Video 标签
</video>
</div>
</div>
);
}

View File

@ -281,7 +281,7 @@ export default function PersonSpace() {
</div>
<ul className="flex mt-8">
<li
className="flex flex-col items-center mr-6"
className="flex flex-col items-center mr-4"
onClick={() =>
setMaskVisible({ visible: true, type: "weChat" })
}
@ -298,7 +298,7 @@ export default function PersonSpace() {
placeholder=""
/>
</div>
<p className="text-xs">查看微信</p>
<p className="text-xs whitespace-nowrap">查看微信</p>
</li>
<li
className="flex flex-col items-center mr-6"
@ -318,7 +318,7 @@ export default function PersonSpace() {
placeholder=""
/>
</div>
<p className="text-xs">
<p className="text-xs whitespace-nowrap">
{streamerInfo?.is_ironfanship_unlocked === 1
? "已是铁粉"
: "成为铁粉"}
@ -362,7 +362,7 @@ export default function PersonSpace() {
placeholder=""
/>
</div>
<p className="text-xs">
<p className="text-xs whitespace-nowrap">
{streamerInfo?.is_superfanship_unlocked === 1
? "尊贵超粉"
: "成为超粉"}
@ -387,7 +387,7 @@ export default function PersonSpace() {
placeholder=""
/>
</div>
<p className="text-xs">举报</p>
<p className="text-xs whitespace-nowrap">举报</p>
</li>
{streamerInfo?.visitor_role === 3 && (
<li
@ -406,7 +406,7 @@ export default function PersonSpace() {
placeholder=""
/>
</div>
<p className="text-xs">审核未通过</p>
<p className="text-xs whitespace-nowrap">审核未通过</p>
</li>
)}
</ul>

View File

@ -7,6 +7,7 @@ import { faAngleRight, faAngleLeft } from "@fortawesome/free-solid-svg-icons";
import UploadImgs from "@/components/UploadImgs";
import requireAPI from "@/utils/requireAPI";
import { multiUploadImage } from "@/utils/upload";
import { getVideoBase64 } from "@/utils/tools";
export default function EditStreamerMedia() {
const router = useRouter();
//
@ -92,14 +93,17 @@ export default function EditStreamerMedia() {
},
}));
} else {
creatVideoCanvas(file);
setFormData((old) => ({
...old,
displayVideo: {
url: frameImageUrl,
file: Array.from(e.target.files),
},
}));
const url = URL.createObjectURL(file);
getVideoBase64(url).then((src) => {
setFrameImageUrl(src);
setFormData((old) => ({
...old,
displayVideo: {
url: src,
file: Array.from(e.target.files),
},
}));
});
}
setLoading(false);
};
@ -189,29 +193,6 @@ export default function EditStreamerMedia() {
}
};
const creatVideoCanvas = (file) => {
if (typeof window == "undefined") return;
const videoD = document.getElementById("video_edit_streamer");
const url = URL.createObjectURL(file);
videoD.src = url;
videoD.addEventListener("loadeddata", function () {
videoD.pause();
videoD.currentTime = 1;
});
videoD.addEventListener("seeked", function () {
const canvas = document.createElement("canvas");
canvas.width = videoD.videoWidth;
canvas.height = videoD.videoHeight;
canvas
.getContext("2d")
.drawImage(videoD, 0, 0, canvas.width, canvas.height);
const canvasImg = canvas.toDataURL();
setFrameImageUrl(canvasImg);
// URL
URL.revokeObjectURL(url);
});
};
useEffect(() => {
setFormData((old) => ({ ...old, imageAssets: oldPhotos }));
}, [oldPhotos]);
@ -364,12 +345,6 @@ export default function EditStreamerMedia() {
</Button>
</div>
</div>
<div className="hidden">
<video id="video_edit_streamer" controls name="media">
<source />
您的浏览器不支持 Video 标签
</video>
</div>
</div>
);
}

View File

@ -2,7 +2,7 @@
import React, { useState, useEffect } from "react";
import { Image } from "antd-mobile";
import { get,save } from "@/utils/storeInfo";
import { get, save } from "@/utils/storeInfo";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faAngleUp, faClose } from "@fortawesome/free-solid-svg-icons";
export default function AddToHome() {
@ -24,7 +24,7 @@ export default function AddToHome() {
fit="cover"
className="rounded-md mr-3"
// src={process.env.NEXT_PUBLIC_WEB_ASSETS_URL+"/icons/nodata.png"}
src={process.env.NEXT_PUBLIC_WEB_ASSETS_URL+"/images/icon.png"}
src={process.env.NEXT_PUBLIC_WEB_ASSETS_URL + "/images/icon.png"}
placeholder=""
width={46}
height={46}
@ -34,11 +34,11 @@ export default function AddToHome() {
建议您添加<span className="text-primary">铁粉空间</span>
到手机桌面
</p>
<p className="text-[#ffffffb2] text-xs">以便随时关注圈子动态</p>
<p className="text-[#ffffffb2] text-xs">以便随时关注空间动态</p>
</div>
</div>
<div
className="bg-primary px-4 py-2 rounded-full font-bold"
className="bg-primary px-4 py-2 rounded-full font-bold whitespace-nowrap"
onClick={() => setFirstLogin((old) => ({ ...old, second: true }))}
>
去添加
@ -66,7 +66,9 @@ export default function AddToHome() {
<Image
fit="cover"
// src={process.env.NEXT_PUBLIC_WEB_ASSETS_URL+"/icons/nodata.png"}
src={process.env.NEXT_PUBLIC_WEB_ASSETS_URL+"/icons/ios_share.png"}
src={
process.env.NEXT_PUBLIC_WEB_ASSETS_URL + "/icons/ios_share.png"
}
placeholder=""
width={32}
height={32}
@ -76,7 +78,9 @@ export default function AddToHome() {
<Image
fit="contain"
// src={process.env.NEXT_PUBLIC_WEB_ASSETS_URL+"/icons/nodata.png"}
src={process.env.NEXT_PUBLIC_WEB_ASSETS_URL+"/images/addToHome.png"}
src={
process.env.NEXT_PUBLIC_WEB_ASSETS_URL + "/images/addToHome.png"
}
placeholder=""
width={54}
height={70}

View File

@ -11,9 +11,9 @@ export default function OwnInput({
name,
placeholder,
clearable,
disabled,
disabled = false,
className,
inputClassName,
inputClassName = "",
fontSize,
id,
}) {

View File

@ -1,9 +1,11 @@
"use client";
import React, { useEffect, useState } from "react";
import { DotLoading, Image, Toast, ImageViewer, Modal } from "antd-mobile";
import { DotLoading, Image, List, ImageViewer, Modal } from "antd-mobile";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faAdd, faClose, faPlay } from "@fortawesome/free-solid-svg-icons";
import { getVideoBase64 } from "@/utils/tools";
import { DragDropContext, Draggable, Droppable } from "@hello-pangea/dnd";
export default function UploadImgs({
assets,
getImgs,
@ -20,7 +22,9 @@ export default function UploadImgs({
const [frameImage, setFrameImage] = useState({ src: null, h: 0, w: 0 });
useEffect(() => {
if (existImages.length > 0) {
setFilesUrls(existImages.map((it) => it.url));
setFilesUrls(
existImages.map((it, index) => ({ url: it.url, id: `${index}` }))
);
}
if (videoSrc) {
setVideoUrl(videoSrc);
@ -37,7 +41,10 @@ export default function UploadImgs({
var videoObj = document.createElement("video");
const eles = Array.from(e.target.files);
if (type == 1) {
const newFils = eles.map((it) => URL.createObjectURL(it));
const newFils = eles.map((it, index) => ({
url: URL.createObjectURL(it),
id: "new" + index,
}));
setFilesUrls((old) => [...old, ...newFils]);
} else {
videoObj.onloadedmetadata = function (evt) {
@ -86,7 +93,13 @@ export default function UploadImgs({
} else {
Modal.show({
content: (
<video autoPlay>
<video
autoPlay
playsInline
controls
muted={true}
controlslist="nodownload"
>
<source src={videoUrl} />
</video>
),
@ -100,70 +113,105 @@ export default function UploadImgs({
};
const creatVideoCanvas = (file) => {
if (typeof window == "undefined") return;
const videoD = document.getElementById("video_upload");
// const videoD = document.getElementById("video_upload");
const url = URL.createObjectURL(file);
videoD.src = url;
videoD.addEventListener("loadeddata", function () {
videoD.pause();
videoD.currentTime = 1;
// videoD.src = url;
getVideoBase64(url).then((src) => {
setFrameImage((old) => ({ ...old, src }));
setFilesUrls([{ url: src, id: "0" }]);
});
videoD.addEventListener("seeked", function () {
const canvas = document.createElement("canvas");
canvas.width = videoD.videoWidth;
canvas.height = videoD.videoHeight;
canvas
.getContext("2d")
.drawImage(videoD, 0, 0, canvas.width, canvas.height);
const canvasImg = canvas.toDataURL("image/png");
setFrameImage((old) => ({ ...old, src: canvasImg }));
setFilesUrls([canvasImg]);
// 释放URL对象
URL.revokeObjectURL(url);
};
const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
const onDragEnd = (result) => {
if (!result.destination) return;
const newList = reorder(
assets,
result.source.index,
result.destination.index
);
getImgs([...newList]);
// const newArr=newList.map((it,index)=>({...it,url:it.url||URL.createObjectURL(it)}))
const newArr = newList.map((it, index) => {
if (it.url) {
return { url: it.url, id: `${index}` };
} else {
return { url: URL.createObjectURL(it), id: `${index}` };
}
});
setFilesUrls(newArr);
};
return (
<div>
<div className="grid grid-cols-4 gap-1">
{filesUrls.map((item, index) => {
return (
<div key={index} className="rounded relative">
<div
onClick={() => showPhotos(filesUrls, index)}
style={{ height: "calc(25vw - 0.75rem)" }}
>
<Image
src={item}
width="100%"
height="100%"
className="rounded"
fit="cover"
/>
</div>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable direction="horizontal" droppableId="droppable">
{(droppableProvided) => (
<div
className="mb-2 grid grid-cols-4 gap-1"
ref={droppableProvided.innerRef}
>
{filesUrls.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={{
...provided.draggableProps.style,
opacity: snapshot.isDragging ? 0.8 : 1,
}}
>
<div key={index} className="rounded relative">
<div
onClick={() => showPhotos(filesUrls, index)}
style={{ height: "calc(25vw - 0.75rem)" }}
>
<Image
src={item.url}
width="100%"
height="100%"
className="rounded"
fit="cover"
/>
</div>
<div
className="h-6 w-6 bg-[#33333380] absolute top-0 right-0 flex justify-center items-center rounded-bl"
onClick={() => handleRemoveItem(index)}
>
<FontAwesomeIcon icon={faClose} size="xl" />
</div>
{type == 2 && (
<div
className="absolute top-1/2 left-1/2 flex justify-center items-center -mt-2 -ml-1"
onClick={() => showPhotos(filesUrls)}
>
<FontAwesomeIcon icon={faPlay} size="xl" />
</div>
)}
<div
className="h-6 w-6 bg-[#33333380] absolute top-0 right-0 flex justify-center items-center rounded-bl"
onClick={() => handleRemoveItem(index)}
>
<FontAwesomeIcon icon={faClose} size="xl" />
</div>
{type == 2 && (
<div
className="absolute top-1/2 left-1/2 flex justify-center items-center -mt-2 -ml-1"
onClick={() => showPhotos(filesUrls)}
>
<FontAwesomeIcon icon={faPlay} size="xl" />
</div>
)}
</div>
</div>
)}
</Draggable>
))}
{droppableProvided.placeholder}
</div>
);
})}
{loading && (
<div className="rounded border-[#ffffff80] text-[#ffffff80] flex flex-col justify-center items-center">
<DotLoading />
<p>上传中</p>
</div>
)}
)}
</Droppable>
</DragDropContext>
{loading && (
<div className="rounded border-[#ffffff80] text-[#ffffff80] flex flex-col justify-center items-center">
<DotLoading />
<p>上传中</p>
</div>
)}
<div className="grid grid-cols-4 gap-1">
{type == 2 && filesUrls.length > 0 ? null : (
<>
<label htmlFor="uploadAvatarBtn">
@ -189,12 +237,6 @@ export default function UploadImgs({
</>
)}
</div>
<div className="hidden">
<video id="video_upload" controls autoPlay name="media">
<source />
您的浏览器不支持 Video 标签
</video>
</div>
</div>
);
}

View File

@ -92,3 +92,62 @@ export function utf8Length(str) {
}
return length;
}
export function getVideoBase64(url) {
return new Promise((resolve, reject) => {
// 创建 video 元素
const video = document.createElement("video");
// 设置跨域属性
video.crossOrigin = "anonymous";
// 设置视频 URL
video.src = url;
// 设置视频画面宽度
video.width = "90";
// 设置视频画面高度
video.height = "90";
// 设置视频播放位置为第一帧
video.currentTime = 1;
// 设置预加载方式为自动加载
video.preload = "auto";
// 创建 canvas 元素
const canvas = document.createElement("canvas");
// 将 canvas 的宽度和高度设置为视频画面的宽度和高度
canvas.width = video.width;
canvas.height = video.height;
// 判断是否为 iOS 系统
if (/(iPad|iPhone|iPod)/gi.test(navigator.userAgent)) {
// 在 iOS 系统中,需要手动触发视频加载,然后设置自动播放和静音属性
video.load();
// video.autoplay = true;
// 在 iOS 系统中,监听 loadedmetadata 事件可以保证视频数据已经全部加载完毕
}
video.muted = true;
video.addEventListener("loadeddata", function () {
setTimeout(function () {
console.log("监听到了listen:", "loadeddata");
canvas
.getContext("2d")
.drawImage(video, 0, 0, canvas.width, canvas.height); //绘制canvas
const dataURL = canvas.toDataURL("image/jpeg"); //转换为base64
video.setAttribute("poster", dataURL);
console.log(dataURL, "dataURL");
resolve(dataURL);
// 清除内存
canvas.remove();
video.remove();
}, 100);
});
// 监听视频事件,在发生错误时 Promise 会 reject
video.addEventListener("error", function () {
reject(new Error("视频加载错误"));
// 清除内存
canvas.remove();
video.remove();
});
});
}

View File

@ -85,6 +85,7 @@ export const handleShowVideo = (video) => {
{/* <Video as={ReactPlayer} src={video.mp4} /> */}
<Player
src={video.mp4}
controlslist="nodownload"
poster={video?.url.src}
blurDataURL={video?.url.blurDataURL}
autoPlay={true}