diff --git a/site/docs/overview.en-US.md b/site/docs/overview.en-US.md index 3b54b31a0..8fc934ff3 100644 --- a/site/docs/overview.en-US.md +++ b/site/docs/overview.en-US.md @@ -375,6 +375,13 @@ spline: explain

Tag

+
+ + + +

Watermark

+
+

Feedback13

diff --git a/site/docs/overview.md b/site/docs/overview.md index 2c41db21d..7de7b1234 100644 --- a/site/docs/overview.md +++ b/site/docs/overview.md @@ -240,7 +240,7 @@ spline: explain -

数据展示19

+

数据展示20

@@ -375,6 +375,13 @@ spline: explain

Tag 标签

+
+ + + +

Watermark 水印

+
+

反馈13

diff --git a/site/mobile/mobile.config.js b/site/mobile/mobile.config.js index cc6ed8825..ffb9612ae 100644 --- a/site/mobile/mobile.config.js +++ b/site/mobile/mobile.config.js @@ -410,5 +410,11 @@ export default { name: 'date-time-picker', component: () => import('tdesign-mobile-react/date-time-picker/_example/index.tsx'), }, + { + title: 'Watermark 水印', + titleEn: 'Watermark', + name: 'watermark', + component: () => import('tdesign-mobile-react/watermark/_example/index.tsx'), + }, ], }; diff --git a/site/style/mobile/demo.less b/site/style/mobile/demo.less index 1271515a9..184cf6442 100644 --- a/site/style/mobile/demo.less +++ b/site/style/mobile/demo.less @@ -41,7 +41,7 @@ line-height: 22px; } - &__slot { + &__slot:not(:empty) { margin-top: 16px; &.with-padding { diff --git a/site/web/site.config.js b/site/web/site.config.js index 7b68fd0a8..9b2b9cfa1 100644 --- a/site/web/site.config.js +++ b/site/web/site.config.js @@ -479,6 +479,14 @@ export const docs = [ component: () => import('tdesign-mobile-react/tag/tag.md'), componentEn: () => import('tdesign-mobile-react/tag/tag.en-US.md'), }, + { + title: 'Watermark 水印', + titleEn: 'Watermark', + name: 'watermark', + path: '/mobile-react/components/watermark', + component: () => import('tdesign-mobile-react/watermark/watermark.md'), + componentEn: () => import('tdesign-mobile-react/watermark/watermark.en-US.md'), + }, ], }, { diff --git a/src/_common b/src/_common index 2030d2719..8538ba896 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit 2030d2719e4253ca8a0539a5c67a182274c2c7b6 +Subproject commit 8538ba896ad100fd716edbd7252fe0d06da8447a diff --git a/src/hooks/useVariables.ts b/src/hooks/useVariables.ts new file mode 100644 index 000000000..709281b19 --- /dev/null +++ b/src/hooks/useVariables.ts @@ -0,0 +1,96 @@ +import { useMemo, useState } from 'react'; +import { THEME_MODE } from '../_common/js/common'; +import getColorTokenColor from '../_common/js/utils/getColorTokenColor'; +import useMutationObservable from './useMutationObserver'; +import { canUseDocument } from '../_util/dom'; + +const DEFAULT_OPTIONS = { + debounceTime: 250, + config: { + attributes: true, + }, +}; + +/** + * useVariables Hook - 监听CSS变量变化并返回实时值 + * @param variables CSS 变量名对象,键为返回对象的属性名,值为CSS变量名(带--前缀) + * @param targetElement 监听的元素,默认为 document?.documentElement + * @returns 包含每个变量当前值的ref对象 + * @example + * const { textColor, brandColor } = useVariables({ + * textColor: '--td-text-color-primary', + * brandColor: '--td-brand-color', + * }); + * + * // 使用变量值 + * console.log(textColor.current); // 获取当前文本颜色 + */ +function useVariables>( + variables: T, + targetElement?: HTMLElement, +): Record { + const [, forceUpdate] = useState>({}); + + if (canUseDocument && !targetElement) { + // eslint-disable-next-line no-param-reassign + targetElement = document?.documentElement; + } + + // 确保 variables 参数有效 + if (!variables || Object.keys(variables).length === 0) { + throw new Error('useVariables: variables parameter cannot be empty'); + } + + const refs = useMemo(() => { + const values = {} as Record; + + // 为每个变量创建ref并获取初始值 + Object.entries(variables).forEach(([key, varName]) => { + try { + const initialValue = getColorTokenColor(varName); + values[key as keyof T] = initialValue; + } catch (error) { + console.warn(`Failed to get initial value for CSS variable ${varName}:`, error); + values[key as keyof T] = ''; + } + }); + + return values; + }, [variables]); + + // 缓存更新函数,避免每次渲染都创建新函数 + const updateVariables = () => { + try { + Object.entries(variables).forEach(([key, varName]) => { + const newValue = getColorTokenColor(varName); + if (refs[key as keyof T] && refs[key as keyof T] !== newValue) { + refs[key as keyof T] = newValue; + } + }); + forceUpdate({}); + } catch (error) { + console.warn('Failed to update CSS variables:', error); + } + }; + + useMutationObservable( + targetElement, + (mutationsList) => { + // 使用 for 循环而不是 some,提高性能 + for (const mutation of mutationsList) { + if (mutation.type === 'attributes' && mutation.attributeName === THEME_MODE) { + updateVariables(); + return; + } + } + }, + DEFAULT_OPTIONS, + ); + + // @ts-expect-error + if (!canUseDocument) return {}; + + return refs; +} + +export default useVariables; diff --git a/src/index.ts b/src/index.ts index b41440082..b70d3b4ac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,7 +41,7 @@ export * from './tree-select'; export * from './upload'; /** - * 数据展示(19个) + * 数据展示(20个) */ export * from './avatar'; export * from './badge'; @@ -62,6 +62,7 @@ export * from './sticky'; export * from './swiper'; export * from './table'; export * from './tag'; +export * from './watermark'; /** * 反馈(13个) diff --git a/src/watermark/Watermark.tsx b/src/watermark/Watermark.tsx new file mode 100644 index 000000000..183f407ea --- /dev/null +++ b/src/watermark/Watermark.tsx @@ -0,0 +1,200 @@ +/* eslint-disable no-nested-ternary */ +import React, { useCallback, useState, useEffect, useRef } from 'react'; +import classNames from 'classnames'; +import generateBase64Url from '../_common/js/watermark/generateBase64Url'; +import randomMovingStyle from '../_common/js/watermark/randomMovingStyle'; +import injectStyle from '../_common/js/utils/injectStyle'; +import { StyledProps } from '../common'; +import useConfig from '../hooks/useConfig'; +import useMutationObserver from '../hooks/useMutationObserver'; +import { TdWatermarkProps } from './type'; +import { watermarkDefaultProps } from './defaultProps'; +import { getStyleStr } from './utils'; +import useDefaultProps from '../hooks/useDefaultProps'; +import useVariables from '../hooks/useVariables'; +import parseTNode from '../_util/parseTNode'; + +export interface WatermarkProps extends TdWatermarkProps, StyledProps {} + +const Watermark: React.FC = (originalProps) => { + const props = useDefaultProps(originalProps, watermarkDefaultProps); + const { + alpha, + x = 200, + y = 210, + width = 120, + height = 60, + rotate: tempRotate, + zIndex = 10, + lineSpace, + isRepeat, + removable, + movable, + moveInterval, + offset = [], + content, + children, + watermarkContent, + layout, + className, + style = {}, + } = props; + + const { classPrefix } = useConfig(); + + let gapX = x; + let gapY = y; + let rotate = tempRotate; + if (movable) { + gapX = 0; + gapY = 0; + rotate = 0; + } + const clsName = `${classPrefix}-watermark`; + const [base64Url, setBase64Url] = useState(''); + const styleStr = useRef(''); + const watermarkRef = useRef(null); + const watermarkImgRef = useRef(null); + const stopObservation = useRef(false); + const offsetLeft = offset[0] || gapX / 2; + const offsetTop = offset[1] || gapY / 2; + const backgroundSize = useRef(`${gapX + width}px`); + + const { fontColor } = useVariables({ + fontColor: '--td-text-color-watermark', + }); + + // 水印节点 - 背景base64 + useEffect(() => { + generateBase64Url( + { + width, + height, + rotate, + lineSpace, + alpha, + gapX, + gapY, + watermarkContent, + offsetLeft, + offsetTop, + fontColor, + layout, + }, + (url, { width }) => { + backgroundSize.current = width ? `${width}px` : null; + setBase64Url(url); + }, + ); + }, [ + width, + height, + rotate, + zIndex, + lineSpace, + alpha, + offsetLeft, + offsetTop, + gapX, + gapY, + watermarkContent, + fontColor, + layout, + ]); + + // 水印节点 - styleStr + useEffect(() => { + styleStr.current = getStyleStr({ + zIndex, + position: 'absolute', + left: 0, + right: 0, + top: 0, + bottom: 0, + width: movable ? `${width}px` : '100%', + height: movable ? `${height}px` : '100%', + backgroundSize: backgroundSize.current || `${gapX + width}px`, + pointerEvents: 'none', + backgroundRepeat: movable ? 'no-repeat' : isRepeat ? 'repeat' : 'no-repeat', + backgroundImage: `url('${base64Url}')`, + animation: movable ? `watermark infinite ${(moveInterval * 4) / 60}s` : 'none', + ...style, + }); + }, [zIndex, gapX, width, movable, isRepeat, base64Url, moveInterval, style, height, backgroundSize]); + + // 水印节点 - 渲染 + const renderWatermark = useCallback(() => { + // 停止监听 + stopObservation.current = true; + // 删除之前 + watermarkImgRef.current?.remove?.(); + watermarkImgRef.current = undefined; + // 创建新的 + watermarkImgRef.current = document.createElement('div'); + watermarkImgRef.current.setAttribute('style', styleStr.current); + watermarkRef.current?.append(watermarkImgRef.current); + // 继续监听 + setTimeout(() => { + stopObservation.current = false; + }); + }, []); + + // 水印节点 - 初始化渲染 + useEffect(() => { + renderWatermark(); + }, [renderWatermark, zIndex, gapX, width, movable, isRepeat, base64Url, moveInterval, style, className]); + + // 水印节点 - 变化时重新渲染 + useMutationObserver(watermarkRef.current, (mutations) => { + if (stopObservation.current) return; + if (removable) return; + mutations.forEach((mutation) => { + // 水印节点被删除 + if (mutation.type === 'childList') { + const removeNodes = mutation.removedNodes; + removeNodes.forEach((node) => { + const element = node as HTMLElement; + if (element === watermarkImgRef.current) { + renderWatermark(); + } + }); + } + // 水印节点其他变化 + if (mutation.target === watermarkImgRef.current) { + renderWatermark(); + } + }); + }); + + // 组件父节点 - 增加keyframes + const parent = useRef(null); + useEffect(() => { + parent.current = watermarkRef.current.parentElement; + const keyframesStyle = randomMovingStyle(); + injectStyle(keyframesStyle); + }, []); + + // 水印节点的父节点 - 防删除 + useMutationObserver(typeof document !== 'undefined' ? document.body : null, (mutations) => { + if (removable) return; + mutations.forEach((mutation) => { + if (mutation.type === 'childList') { + const removeNodes = mutation.removedNodes; + removeNodes.forEach((node) => { + const element = node as HTMLElement; + if (element === watermarkRef.current) { + parent.current.appendChild(element); + } + }); + } + }); + }); + + return ( +
+ {parseTNode(children || content)} +
+ ); +}; + +export default Watermark; diff --git a/src/watermark/__tests__/watermark.test.tsx b/src/watermark/__tests__/watermark.test.tsx new file mode 100644 index 000000000..271909122 --- /dev/null +++ b/src/watermark/__tests__/watermark.test.tsx @@ -0,0 +1,96 @@ +import React from 'react'; +import { beforeAll, beforeEach, describe, expect, test, vi } from '@test/utils'; +import { render } from '@testing-library/react'; +import Watermark from '../Watermark'; + +describe('Watermark', () => { + const childTestID = 'childTestID'; + const mockGetCanvasContext = vi.spyOn(HTMLCanvasElement.prototype, 'getContext'); + const mockGetCanvasToDataURL = vi.spyOn(HTMLCanvasElement.prototype, 'toDataURL'); + + beforeAll(() => { + mockGetCanvasContext.mockReturnValue({ + drawImage: vi.fn(), + getImageData: vi.fn(), + putImageData: vi.fn(), + translate: vi.fn(), + rotate: vi.fn(), + fillRect: vi.fn(), + globalAlpha: 0.5, + font: '', + textAlign: '', + textBaseline: '', + fillStyle: '', + fillText: vi.fn(), + }); + mockGetCanvasToDataURL.mockReturnValue('test'); + }); + + beforeEach(() => { + vi.useFakeTimers(); + }); + + function renderWatermark(watermark) { + const { container } = render(watermark); + return container.firstChild; + } + + test('base', async () => { + const watermark = renderWatermark( + +
+
, + ); + expect(watermark).toHaveClass('t-watermark'); + expect(watermark.lastChild).toHaveStyle({ 'background-repeat': 'repeat' }); + }); + + test('movable ', async () => { + const watermark = renderWatermark( + +
+
, + ); + expect(watermark.lastChild).toHaveStyle({ animation: 'watermark infinite 1s' }); + }); + + test('mutationObserver', async () => { + const wrapper = render( + +
+
, + ); + const child = document.createElement('div'); + child.innerText = 'testing'; + child.id = 'test'; + + const watermarkWrapCls = wrapper.container.querySelector('.test-observer'); + expect(watermarkWrapCls).not.toBeNull(); + const watermarkWrap = wrapper.container.querySelector('.t-watermark'); + const watermarkWrapParent = watermarkWrap.parentElement; + const watermarkEle = watermarkWrap.querySelectorAll('div')[1]; + + // 删除了水印wrap元素,还会被立即追加回去 + watermarkWrapParent.removeChild(watermarkWrap); + const afterWrapRemove = watermarkWrapParent.querySelector('.t-watermark'); + expect(afterWrapRemove).toBeNull(); + await vi.advanceTimersByTime(10); + const waitAfterWrapRemove = watermarkWrapParent.querySelector('.t-watermark'); + expect(waitAfterWrapRemove).not.toBeNull(); + + // 删除了水印元素,还会被立即追加回去 + waitAfterWrapRemove.removeChild(watermarkEle); + expect(waitAfterWrapRemove.querySelectorAll('div').length).toBe(1); + // const afterMarkRemove = waitAfterWrapRemove.querySelectorAll('div')[1]; + // expect(afterMarkRemove).toBeNull(); + await vi.advanceTimersByTime(10); + const waitAfterMarkRemove = waitAfterWrapRemove.querySelectorAll('div')[1]; + expect(waitAfterMarkRemove).not.toBeNull(); + + // 修改水印元素的属性,会立即还原 + watermarkEle.setAttribute('any', '11'); + await vi.advanceTimersByTime(10); + const waitAfterAttrChange = watermarkWrap.querySelectorAll('div')[1]; + expect(waitAfterAttrChange.getAttribute('any')).toBeNull(); + }); +}); diff --git a/src/watermark/_example/base.tsx b/src/watermark/_example/base.tsx new file mode 100644 index 000000000..a3256bee9 --- /dev/null +++ b/src/watermark/_example/base.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { Watermark } from 'tdesign-mobile-react'; + +export default function BaseWatermark() { + return ( + +
+
+ ); +} diff --git a/src/watermark/_example/gray.tsx b/src/watermark/_example/gray.tsx new file mode 100644 index 000000000..0290b7b8a --- /dev/null +++ b/src/watermark/_example/gray.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { Watermark } from 'tdesign-mobile-react'; + +export default function GrayscaleWatermark() { + return ( + +
+
+ ); +} diff --git a/src/watermark/_example/image.tsx b/src/watermark/_example/image.tsx new file mode 100644 index 000000000..b123dd99a --- /dev/null +++ b/src/watermark/_example/image.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { Watermark } from 'tdesign-mobile-react'; + +export default function ImageWatermark() { + return ( + +
+
+ ); +} diff --git a/src/watermark/_example/index.tsx b/src/watermark/_example/index.tsx new file mode 100644 index 000000000..446b31f2b --- /dev/null +++ b/src/watermark/_example/index.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import TDemoBlock from '../../../site/mobile/components/DemoBlock'; +import BaseDemo from './base'; +import ImageDemo from './image'; +import GaryDemo from './gray'; +import MultiLineDemo from './multiLine'; +import MultiLineGaryDemo from './multiLineGray'; +import MovingTextDemo from './movingText'; +import MovingImageDemo from './movingImage'; +import LayoutDemo from './layout'; + +export default function Base() { + return ( +
+

Watermark 标签

+

给页面的某个区域加上水印。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +} diff --git a/src/watermark/_example/layout.tsx b/src/watermark/_example/layout.tsx new file mode 100644 index 000000000..458b45860 --- /dev/null +++ b/src/watermark/_example/layout.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { Watermark } from 'tdesign-mobile-react'; + +export default function LayoutWatermark() { + return ( + <> +
+ Rectangular Grid 矩形布局 +
+ +
+
+ +
+ Hexagonal Grid 六边形网格 +
+ +
+
+ + ); +} diff --git a/src/watermark/_example/movingImage.tsx b/src/watermark/_example/movingImage.tsx new file mode 100644 index 000000000..add40ae74 --- /dev/null +++ b/src/watermark/_example/movingImage.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { Watermark } from 'tdesign-mobile-react'; + +export default function MovingImageWatermark() { + return ( + +
+
+ ); +} diff --git a/src/watermark/_example/movingText.tsx b/src/watermark/_example/movingText.tsx new file mode 100644 index 000000000..c3845a7d6 --- /dev/null +++ b/src/watermark/_example/movingText.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { Watermark } from 'tdesign-mobile-react'; + +export default function MovingTextWatermark() { + return ( + +
+
+ ); +} diff --git a/src/watermark/_example/multiLine.tsx b/src/watermark/_example/multiLine.tsx new file mode 100644 index 000000000..1504425eb --- /dev/null +++ b/src/watermark/_example/multiLine.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { Watermark } from 'tdesign-mobile-react'; + +export default function MultilineWatermark() { + return ( + +
+
+ ); +} diff --git a/src/watermark/_example/multiLineGray.tsx b/src/watermark/_example/multiLineGray.tsx new file mode 100644 index 000000000..9342268dc --- /dev/null +++ b/src/watermark/_example/multiLineGray.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { Watermark } from 'tdesign-mobile-react'; + +export default function MultilineWatermark() { + return ( + +
+
+ ); +} diff --git a/src/watermark/defaultProps.ts b/src/watermark/defaultProps.ts new file mode 100644 index 000000000..6b8e14f82 --- /dev/null +++ b/src/watermark/defaultProps.ts @@ -0,0 +1,16 @@ +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TdWatermarkProps } from './type'; + +export const watermarkDefaultProps: TdWatermarkProps = { + alpha: 1, + isRepeat: true, + layout: 'rectangular', + lineSpace: 16, + movable: false, + moveInterval: 3000, + removable: true, + rotate: -22, +}; diff --git a/src/watermark/index.ts b/src/watermark/index.ts new file mode 100644 index 000000000..739ea3b84 --- /dev/null +++ b/src/watermark/index.ts @@ -0,0 +1,10 @@ +import _Watermark from './Watermark'; + +import './style/index.js'; + +export type { WatermarkProps } from './Watermark'; + +export * from './type'; + +export const Watermark = _Watermark; +export default Watermark; diff --git a/src/watermark/style/css.js b/src/watermark/style/css.js new file mode 100644 index 000000000..6a9a4b132 --- /dev/null +++ b/src/watermark/style/css.js @@ -0,0 +1 @@ +import './index.css'; diff --git a/src/watermark/style/index.js b/src/watermark/style/index.js new file mode 100644 index 000000000..04c9fdd7a --- /dev/null +++ b/src/watermark/style/index.js @@ -0,0 +1 @@ +import '../../_common/style/mobile/components/watermark/_index.less'; diff --git a/src/watermark/type.ts b/src/watermark/type.ts new file mode 100644 index 000000000..a42173293 --- /dev/null +++ b/src/watermark/type.ts @@ -0,0 +1,127 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TNode } from '../common'; + +export interface TdWatermarkProps { + /** + * 水印整体透明度,取值范围 [0-1] + * @default 1 + */ + alpha?: number; + /** + * 水印所覆盖的内容节点,同 `content` + */ + children?: TNode; + /** + * 水印所覆盖的内容节点 + */ + content?: TNode; + /** + * 水印高度 + */ + height?: number; + /** + * 水印是否重复出现 + * @default true + */ + isRepeat?: boolean; + /** + * 水印的布局方式,rectangular:矩形,即横平竖直的水印;hexagonal:六边形,即错位的水印 + * @default rectangular + */ + layout?: 'rectangular' | 'hexagonal'; + /** + * 行间距,只作用在多行(`content` 配置为数组)情况下 + * @default 16 + */ + lineSpace?: number; + /** + * 水印是否可移动 + * @default false + */ + movable?: boolean; + /** + * 水印发生运动位移的间隙,单位:毫秒 + * @default 3000 + */ + moveInterval?: number; + /** + * 水印在画布上绘制的水平和垂直偏移量,正常情况下水印绘制在中间位置,即 `offset = [gapX / 2, gapY / 2]` + */ + offset?: Array; + /** + * 水印是否可被删除 + * @default true + */ + removable?: boolean; + /** + * 水印旋转的角度,单位 ° + * @default -22 + */ + rotate?: number; + /** + * 水印内容,需要显示多行情况下可配置为数组 + */ + watermarkContent?: WatermarkText | WatermarkImage | Array; + /** + * 水印宽度 + */ + width?: number; + /** + * 水印之间的水平间距 + */ + x?: number; + /** + * 水印之间的垂直间距 + */ + y?: number; + /** + * 水印元素的 `z-index`,默认值写在 CSS 中 + */ + zIndex?: number; +} + +export interface WatermarkText { + /** + * 水印文本文字颜色 + * @default rgba(0,0,0,0.1) + */ + fontColor?: string; + /** + * 水印文本文字字体 + * @default '' + */ + fontFamily?: string; + /** + * 水印文本文字大小 + * @default 16 + */ + fontSize?: number; + /** + * 水印文本文字粗细 + * @default normal + */ + fontWeight?: 'normal' | 'lighter' | 'bold' | 'bolder'; + /** + * 水印文本内容 + * @default '' + */ + text?: string; +} + +export interface WatermarkImage { + /** + * 水印图片是否需要灰阶显示 + * @default false + */ + isGrayscale?: boolean; + /** + * 水印图片源地址,为了显示清楚,建议导出 2 倍或 3 倍图 + * @default '' + */ + url?: string; +} diff --git a/src/watermark/utils.ts b/src/watermark/utils.ts new file mode 100644 index 000000000..3e80996bf --- /dev/null +++ b/src/watermark/utils.ts @@ -0,0 +1,7 @@ +const toLowercaseSeparator = (key: string) => key.replace(/([A-Z])/g, '-$1').toLowerCase(); + +// style对象转字符串 +export const getStyleStr = (style: React.CSSProperties): string => + Object.keys(style) + .map((key: keyof React.CSSProperties) => `${toLowercaseSeparator(key)}: ${style[key]};`) + .join(' '); diff --git a/src/watermark/watermark.en-US.md b/src/watermark/watermark.en-US.md new file mode 100644 index 000000000..8a450ed2d --- /dev/null +++ b/src/watermark/watermark.en-US.md @@ -0,0 +1,45 @@ +:: BASE_DOC :: + +## API + + +### Watermark Props + +name | type | default | description | required +-- | -- | -- | -- | -- +className | String | - | className of component | N +style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N +alpha | Number | 1 | \- | N +children | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +content | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +height | Number | - | \- | N +isRepeat | Boolean | true | \- | N +layout | String | rectangular | options: rectangular/hexagonal | N +lineSpace | Number | 16 | \- | N +movable | Boolean | false | \- | N +moveInterval | Number | 3000 | \- | N +offset | Array | - | Typescript:`Array` | N +removable | Boolean | true | \- | N +rotate | Number | -22 | \- | N +watermarkContent | Object / Array | - | Typescript:`WatermarkText\|WatermarkImage\|Array` | N +width | Number | - | \- | N +x | Number | - | \- | N +y | Number | - | \- | N +zIndex | Number | - | \- | N + +### WatermarkText + +name | type | default | description | required +-- | -- | -- | -- | -- +fontColor | String | rgba(0,0,0,0.1) | \- | N +fontFamily | String | - | font-family configuration for watermark text | N +fontSize | Number | 16 | \- | N +fontWeight | String | normal | options: normal/lighter/bold/bolder | N +text | String | - | \- | N + +### WatermarkImage + +name | type | default | description | required +-- | -- | -- | -- | -- +isGrayscale | Boolean | false | \- | N +url | String | - | \- | N diff --git a/src/watermark/watermark.md b/src/watermark/watermark.md new file mode 100644 index 000000000..a7189ecf3 --- /dev/null +++ b/src/watermark/watermark.md @@ -0,0 +1,45 @@ +:: BASE_DOC :: + +## API + + +### Watermark Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +className | String | - | 类名 | N +style | Object | - | 样式,TS 类型:`React.CSSProperties` | N +alpha | Number | 1 | 水印整体透明度,取值范围 [0-1] | N +children | TNode | - | 水印所覆盖的内容节点,同 `content`。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +content | TNode | - | 水印所覆盖的内容节点。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +height | Number | - | 水印高度 | N +isRepeat | Boolean | true | 水印是否重复出现 | N +layout | String | rectangular | 水印的布局方式,rectangular:矩形,即横平竖直的水印;hexagonal:六边形,即错位的水印。可选项:rectangular/hexagonal | N +lineSpace | Number | 16 | 行间距,只作用在多行(`content` 配置为数组)情况下 | N +movable | Boolean | false | 水印是否可移动 | N +moveInterval | Number | 3000 | 水印发生运动位移的间隙,单位:毫秒 | N +offset | Array | - | 水印在画布上绘制的水平和垂直偏移量,正常情况下水印绘制在中间位置,即 `offset = [gapX / 2, gapY / 2]`。TS 类型:`Array` | N +removable | Boolean | true | 水印是否可被删除 | N +rotate | Number | -22 | 水印旋转的角度,单位 ° | N +watermarkContent | Object / Array | - | 水印内容,需要显示多行情况下可配置为数组。TS 类型:`WatermarkText\|WatermarkImage\|Array` | N +width | Number | - | 水印宽度 | N +x | Number | - | 水印之间的水平间距 | N +y | Number | - | 水印之间的垂直间距 | N +zIndex | Number | - | 水印元素的 `z-index`,默认值写在 CSS 中 | N + +### WatermarkText + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +fontColor | String | rgba(0,0,0,0.1) | 水印文本文字颜色 | N +fontFamily | String | - | 水印文本文字字体 | N +fontSize | Number | 16 | 水印文本文字大小 | N +fontWeight | String | normal | 水印文本文字粗细。可选项:normal/lighter/bold/bolder | N +text | String | - | 水印文本内容 | N + +### WatermarkImage + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +isGrayscale | Boolean | false | 水印图片是否需要灰阶显示 | N +url | String | - | 水印图片源地址,为了显示清楚,建议导出 2 倍或 3 倍图 | N