Skip to content

Commit

Permalink
Merge pull request #1462 from easyops-cn/steve/dashboard-fix
Browse files Browse the repository at this point in the history
feat(eo-carousel-text): use speed instead of duration
  • Loading branch information
qiaofengxi authored Dec 30, 2024
2 parents bb60fb5 + 8f8843f commit f0b5659
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 38 deletions.
6 changes: 1 addition & 5 deletions bricks/presentational/docs/eo-carousel-text.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,5 @@
```yaml preview
brick: eo-carousel-text
properties:
text: Hello world
animationDuration: 3000
fontColor: var(--antd-text-color)
fontSize: 20px
containerWidth: 100%
text: Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
```
56 changes: 52 additions & 4 deletions bricks/presentational/src/carousel-text/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,70 @@ import type { EoCarouselText } from "./index.js";

jest.mock("@next-core/theme", () => ({}));

jest.mock(
"resize-observer-polyfill",
() =>
class {
#callback: ResizeObserverCallback;
constructor(callback: ResizeObserverCallback) {
this.#callback = callback;
}
disconnect() {}
observe() {
Promise.resolve().then(() => {
this.#callback([], this);
});
}
unobserve() {}
}
);

describe("eo-carousel-text", () => {
test("basic usage", async () => {
const element = document.createElement(
"eo-carousel-text"
) as EoCarouselText;

expect(element.shadowRoot).toBeFalsy();

act(() => {
document.body.appendChild(element);
});
expect(element.shadowRoot?.childNodes.length).toBeGreaterThan(1);

const container = element.shadowRoot.querySelector(
".container"
) as HTMLElement;
Object.defineProperty(container, "offsetWidth", {
value: 400,
writable: true,
});

Object.defineProperty(
element.shadowRoot.querySelector(".scroll") as HTMLElement,
"offsetWidth",
{
value: 600,
writable: true,
}
);

expect(container.classList.contains("ready")).toBe(false);

await act(async () => {
await (global as any).flushPromises();
});

expect(container.classList.contains("ready")).toBe(true);
expect(
(container as HTMLElement).style.getPropertyValue("--transform-start")
).toBe("translateX(400px)");
expect(
(container as HTMLElement).style.getPropertyValue("--transform-end")
).toBe("translateX(-600px)");
expect(
(container as HTMLElement).style.getPropertyValue("--transform-duration")
).toBe("10s");

act(() => {
document.body.removeChild(element);
});
expect(element.shadowRoot?.childNodes.length).toBe(0);
});
});
83 changes: 66 additions & 17 deletions bricks/presentational/src/carousel-text/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React, { CSSProperties } from "react";
import React, { CSSProperties, useEffect, useRef } from "react";
import { createDecorators } from "@next-core/element";
import { ReactNextElement } from "@next-core/react-element";
import "@next-core/theme";
import ResizeObserver from "resize-observer-polyfill";
import styleText from "./styles.shadow.css";
import { useState } from "react";

const { defineElement, property } = createDecorators();

Expand All @@ -25,34 +27,34 @@ class EoCarouselText extends ReactNextElement {
* @default "100%"
*/
@property()
accessor containerWidth: CSSProperties["width"] = "100%";
accessor containerWidth: CSSProperties["width"];

/**
* 字体大小
* @default "14px"
* @default "var(--normal-font-size)"
*/
@property()
accessor fontSize: CSSProperties["fontSize"] = "14px";
accessor fontSize: CSSProperties["fontSize"];

/**
* 字体颜色
* @default "black"
* @default "var(--text-color-default)"
*/
@property()
accessor fontColor: CSSProperties["color"] = "black";
accessor fontColor: CSSProperties["color"];

/**
* 动画周期所需时间,单位ms
* @default 3000
* 移动速度,单位 px/s
* @default 100
*/
@property()
accessor animationDuration: number = 3000;
accessor speed: number;

render() {
return (
<EoCarouselTextComponent
text={this.text}
animationDuration={this.animationDuration}
speed={this.speed}
fontColor={this.fontColor}
fontSize={this.fontSize}
containerWidth={this.containerWidth}
Expand All @@ -63,24 +65,71 @@ class EoCarouselText extends ReactNextElement {

export interface EoCarouselTextProps {
text: string;
animationDuration: number;
speed?: number;
fontColor: CSSProperties["color"];
fontSize: CSSProperties["fontSize"];
containerWidth: CSSProperties["width"];
}

export function EoCarouselTextComponent(props: EoCarouselTextProps) {
const { text, animationDuration, fontColor, fontSize, containerWidth } =
props;
export function EoCarouselTextComponent({
text,
fontColor,
fontSize,
containerWidth,
speed: _speed,
}: EoCarouselTextProps) {
const speed = _speed ?? 100;
const containerRef = useRef<HTMLDivElement>(null);
const scrollRef = useRef<HTMLDivElement>(null);
const [start, setStart] = useState(0);
const [end, setEnd] = useState(0);

useEffect(() => {
const container = containerRef.current;
const scroll = scrollRef.current;

if (!container || !scroll) {
return;
}
const observer = new ResizeObserver(() => {
setStart(container.offsetWidth);
setEnd(-scroll.offsetWidth);
});

observer.observe(container);
observer.observe(scroll);

return () => {
observer.disconnect();
};
}, []);

const ready = start && end;

return (
<div className="scrollContainer" style={{ width: containerWidth }}>
<div
className={`container${ready ? " ready" : ""}`}
style={
{
width: containerWidth,
...(ready
? {
"--transform-start": `translateX(${start}px)`,
"--transform-end": `translateX(${end}px)`,
"--transform-duration": `${Math.abs(end - start) / speed}s`,
}
: null),
} as any
}
ref={containerRef}
>
<div
className="scrollText"
className="scroll"
style={{
animationDuration: `${animationDuration}ms`,
color: fontColor,
fontSize: fontSize,
}}
ref={scrollRef}
>
{text}
</div>
Expand Down
29 changes: 17 additions & 12 deletions bricks/presentational/src/carousel-text/styles.shadow.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,34 @@
display: none;
}

.scrollContainer {
.container {
overflow: hidden;
width: 100%;

--transform-start: translateX(0%);
--transform-end: translateX(0%);
--transform-duration: 0s;
}

.container:not(.ready) {
visibility: hidden;
}

.scrollText {
.scroll {
width: max-content;
white-space: nowrap;
overflow: hidden;
animation: scroll 3000ms linear infinite;
animation: scroll var(--transform-duration) linear infinite;
color: var(--text-color-default);
font-size: var(--normal-font-size);
}

@keyframes scroll {
0% {
transform: translateX(100%);
/* 文字出发的位置 */
}

50% {
transform: translateX(0);
/* 文字正常的位置 */
transform: var(--transform-start);
}

100% {
transform: translateX(-100%);
/* 文字终点的位置 */
transform: var(--transform-end);
}
}

0 comments on commit f0b5659

Please sign in to comment.