diff --git a/.gitignore b/.gitignore index 3f885fe4..80e03c3d 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ storage/options.php .phpunit.result.cache .php_cs.cache resources/views/overrides +public/bg diff --git a/resources/assets/src/components/Viewer.tsx b/resources/assets/src/components/Viewer.tsx index 8f9f1b1c..ee06fad3 100644 --- a/resources/assets/src/components/Viewer.tsx +++ b/resources/assets/src/components/Viewer.tsx @@ -1,9 +1,11 @@ import React, { useState, useEffect, useRef } from 'react' import * as skinview3d from 'skinview3d' -import { trans } from '../scripts/i18n' +import { t } from '../scripts/i18n' import styles from './Viewer.scss' import SkinSteve from '../../../misc/textures/steve.png' +export const PICTURES_COUNT = 7 + interface Props { skin?: string cape?: string @@ -40,15 +42,19 @@ const Viewer: React.FC = props => { const stuffRef = useRef(emptyStuff) const [paused, setPaused] = useState(false) + const [running, setRunning] = useState(false) const [reset, setReset] = useState(0) + const [background, setBackground] = useState('#fff') + const [bgPicture, setBgPicture] = useState(0) + const indicator = (() => { const { skin, cape } = props if (skin && cape) { - return `${trans('general.skin')} & ${trans('general.cape')}` + return `${t('general.skin')} & ${t('general.cape')}` } else if (skin) { - return trans('general.skin') + return t('general.skin') } else if (cape) { - return trans('general.cape') + return t('general.cape') } return '' })() @@ -117,12 +123,19 @@ const Viewer: React.FC = props => { viewer.playerObject.skin.slim = !!props.isAlex }, [props.isAlex]) + useEffect(() => { + if (bgPicture !== 0) { + setBackground(`url("${blessing.base_url}/bg/${bgPicture}.png")`) + } + }, [bgPicture]) + const togglePause = () => { setPaused(paused => !paused) viewRef.current.animationPaused = !viewRef.current.animationPaused } const toggleRun = () => { + setRunning(running => !running) const { handles } = stuffRef.current handles.run.paused = !handles.run.paused handles.walk.paused = false @@ -137,52 +150,103 @@ const Viewer: React.FC = props => { setReset(c => c + 1) } + const setWhite = () => setBackground('#fff') + const setGray = () => setBackground('#6c757d') + const setBlack = () => setBackground('#000') + const setPrevPicture = () => { + if (bgPicture <= 1) { + setBgPicture(PICTURES_COUNT) + } else { + setBgPicture(bg => bg - 1) + } + } + const setNextPicture = () => { + if (bgPicture >= PICTURES_COUNT) { + setBgPicture(1) + } else { + setBgPicture(bg => bg + 1) + } + } + return (

- {trans('general.texturePreview')} + {t('general.texturePreview')} {props.showIndicator && ( {indicator} )}

-
+
- {props.children &&
{props.children}
} +
+
+
+
+
+
+ +
+
+ +
+
+ {props.children} +
) } diff --git a/resources/assets/tests/components/Viewer.test.tsx b/resources/assets/tests/components/Viewer.test.tsx index 37086f40..5a608190 100644 --- a/resources/assets/tests/components/Viewer.test.tsx +++ b/resources/assets/tests/components/Viewer.test.tsx @@ -1,7 +1,7 @@ import React from 'react' import { render, fireEvent } from '@testing-library/react' -import { trans } from '@/scripts/i18n' -import Viewer from '@/components/Viewer' +import { t } from '@/scripts/i18n' +import Viewer, { PICTURES_COUNT } from '@/components/Viewer' test('custom footer', () => { const { queryByText } = render(footer) @@ -11,25 +11,25 @@ test('custom footer', () => { describe('indicator', () => { it('hidden by default', () => { const { queryByText } = render() - expect(queryByText(trans('general.skin'))).not.toBeInTheDocument() + expect(queryByText(t('general.skin'))).not.toBeInTheDocument() }) it('nothing', () => { const { queryByText } = render() - expect(queryByText(trans('general.skin'))).not.toBeInTheDocument() - expect(queryByText(trans('general.cape'))).not.toBeInTheDocument() + expect(queryByText(t('general.skin'))).not.toBeInTheDocument() + expect(queryByText(t('general.cape'))).not.toBeInTheDocument() }) it('skin only', () => { const { queryByText } = render() - expect(queryByText(trans('general.skin'))).toBeInTheDocument() - expect(queryByText(trans('general.cape'))).not.toBeInTheDocument() + expect(queryByText(t('general.skin'))).toBeInTheDocument() + expect(queryByText(t('general.cape'))).not.toBeInTheDocument() }) it('cape only', () => { const { queryByText } = render() - expect(queryByText(trans('general.skin'))).not.toBeInTheDocument() - expect(queryByText(trans('general.cape'))).toBeInTheDocument() + expect(queryByText(t('general.skin'))).not.toBeInTheDocument() + expect(queryByText(t('general.cape'))).toBeInTheDocument() }) it('skin and cape', () => { @@ -37,35 +37,94 @@ describe('indicator', () => { , ) expect( - queryByText(`${trans('general.skin')} & ${trans('general.cape')}`), + queryByText(`${t('general.skin')} & ${t('general.cape')}`), ).toBeInTheDocument() - expect(queryByText(trans('general.skin'))).not.toBeInTheDocument() - expect(queryByText(trans('general.cape'))).not.toBeInTheDocument() + expect(queryByText(t('general.skin'))).not.toBeInTheDocument() + expect(queryByText(t('general.cape'))).not.toBeInTheDocument() }) }) describe('actions', () => { it('toggle run', () => { const { getByTitle } = render() - fireEvent.click( - getByTitle(`${trans('general.walk')} / ${trans('general.run')}`), - ) + fireEvent.click(getByTitle(`${t('general.walk')} / ${t('general.run')}`)) }) it('toggle rotation', () => { const { getByTitle } = render() - fireEvent.click(getByTitle(trans('general.rotation'))) + fireEvent.click(getByTitle(t('general.rotation'))) }) it('toggle pause', () => { const { getByTitle } = render() - const icon = getByTitle(trans('general.pause')) + const icon = getByTitle(t('general.pause')) fireEvent.click(icon) expect(icon).toHaveClass('fa-play') }) it('reset', () => { const { getByTitle } = render() - fireEvent.click(getByTitle(trans('general.reset'))) + fireEvent.click(getByTitle(t('general.reset'))) + }) +}) + +describe('background', () => { + it('white', () => { + const { getByTitle, baseElement } = render() + fireEvent.click(getByTitle(t('colors.white'))) + expect( + baseElement.querySelector('.card-body')!.style.background, + ).toBe('rgb(255, 255, 255)') + }) + + it('black', () => { + const { getByTitle, baseElement } = render() + fireEvent.click(getByTitle(t('colors.black'))) + expect( + baseElement.querySelector('.card-body')!.style.background, + ).toBe('rgb(0, 0, 0)') + }) + + it('white', () => { + const { getByTitle, baseElement } = render() + fireEvent.click(getByTitle(t('colors.gray'))) + expect( + baseElement.querySelector('.card-body')!.style.background, + ).toBe('rgb(108, 117, 125)') + }) + + it('previous picture', () => { + const { getByTitle, baseElement } = render() + + fireEvent.click(getByTitle(t('colors.prev'))) + expect( + baseElement.querySelector('.card-body')!.style.background, + ).toBe(`url(/bg/${PICTURES_COUNT}.png)`) + + fireEvent.click(getByTitle(t('colors.prev'))) + expect( + baseElement.querySelector('.card-body')!.style.background, + ).toBe(`url(/bg/${PICTURES_COUNT - 1}.png)`) + }) + + it('next picture', () => { + const { getByTitle, baseElement } = render() + + fireEvent.click(getByTitle(t('colors.next'))) + expect( + baseElement.querySelector('.card-body')!.style.background, + ).toBe('url(/bg/1.png)') + + fireEvent.click(getByTitle(t('colors.next'))) + expect( + baseElement.querySelector('.card-body')!.style.background, + ).toBe('url(/bg/2.png)') + + Array.from({ length: PICTURES_COUNT - 1 }).forEach(() => { + fireEvent.click(getByTitle(t('colors.next'))) + }) + expect( + baseElement.querySelector('.card-body')!.style.background, + ).toBe('url(/bg/1.png)') }) }) diff --git a/resources/lang/en/front-end.yml b/resources/lang/en/front-end.yml index fb0ca824..43ae5d1b 100644 --- a/resources/lang/en/front-end.yml +++ b/resources/lang/en/front-end.yml @@ -305,6 +305,13 @@ general: previews: Texture Previews last-modified: Last Modified +colors: + black: Black + white: White + gray: Gray + prev: Previous Background + next: Next Background + vendor: datatable: search: Search diff --git a/resources/misc/backgrounds/1.png b/resources/misc/backgrounds/1.png new file mode 100755 index 00000000..0b877a1e Binary files /dev/null and b/resources/misc/backgrounds/1.png differ diff --git a/resources/misc/backgrounds/2.png b/resources/misc/backgrounds/2.png new file mode 100755 index 00000000..3f7c6e06 Binary files /dev/null and b/resources/misc/backgrounds/2.png differ diff --git a/resources/misc/backgrounds/3.png b/resources/misc/backgrounds/3.png new file mode 100755 index 00000000..54cf5070 Binary files /dev/null and b/resources/misc/backgrounds/3.png differ diff --git a/resources/misc/backgrounds/4.png b/resources/misc/backgrounds/4.png new file mode 100755 index 00000000..72cbc6c2 Binary files /dev/null and b/resources/misc/backgrounds/4.png differ diff --git a/resources/misc/backgrounds/5.png b/resources/misc/backgrounds/5.png new file mode 100755 index 00000000..ed61b264 Binary files /dev/null and b/resources/misc/backgrounds/5.png differ diff --git a/resources/misc/backgrounds/6.png b/resources/misc/backgrounds/6.png new file mode 100755 index 00000000..d9d6142a Binary files /dev/null and b/resources/misc/backgrounds/6.png differ diff --git a/resources/misc/backgrounds/7.png b/resources/misc/backgrounds/7.png new file mode 100755 index 00000000..179accae Binary files /dev/null and b/resources/misc/backgrounds/7.png differ diff --git a/resources/misc/changelogs/en/5.0.0.md b/resources/misc/changelogs/en/5.0.0.md index 343455e7..5c792bab 100644 --- a/resources/misc/changelogs/en/5.0.0.md +++ b/resources/misc/changelogs/en/5.0.0.md @@ -23,6 +23,7 @@ - Support searching players at players page. - Added Blessing Skin Shell. - Support specifying "from" email address and name when sending email. +- 3D skin viewer can be with background now. ## Tweaked diff --git a/resources/misc/changelogs/zh_CN/5.0.0.md b/resources/misc/changelogs/zh_CN/5.0.0.md index adcdb6b9..bbd6034a 100644 --- a/resources/misc/changelogs/zh_CN/5.0.0.md +++ b/resources/misc/changelogs/zh_CN/5.0.0.md @@ -23,6 +23,7 @@ - 角色页面可进行搜索 - 新增 Blessing Skin Shell - 支持单独指定邮件发件人的地址和名称 +- 3D 皮肤预览现在是带背景的 ## 调整 diff --git a/scripts/build.ps1 b/scripts/build.ps1 index ffde505a..2e867e3f 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -20,6 +20,7 @@ if ($Simple) { # Copy static files Copy-Item -Path ./resources/assets/src/images/bg.png -Destination ./public/app Copy-Item -Path ./resources/assets/src/images/favicon.ico -Destination ./public/app +Copy-Item -Path ./resources/misc/backgrounds/ ./public/bg -Recurse Write-Host 'Static files copied.' -ForegroundColor Green # Write commit ID