-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1016 from XiaoMi/feature/watermark
Feature/watermark
- Loading branch information
Showing
13 changed files
with
335 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import React from 'react' | ||
import PropTypes from 'prop-types' | ||
import WaterMarker from './watermark' | ||
import _ from 'lodash' | ||
class Watermark extends React.Component { | ||
constructor (props) { | ||
super(props) | ||
|
||
this.rootRef = React.createRef() | ||
} | ||
componentDidMount () { | ||
const container = this.rootRef.current | ||
const options = _.cloneDeep(this.props) | ||
delete options.children | ||
|
||
WaterMarker(container, options) | ||
} | ||
render () { | ||
return ( | ||
<div ref={this.rootRef} style={{ overflow: 'hidden' }}> | ||
{this.props.children} | ||
</div> | ||
) | ||
} | ||
} | ||
|
||
Watermark.propTypes = { | ||
id: PropTypes.string, | ||
width: PropTypes.number, | ||
height: PropTypes.number, | ||
textAlign: PropTypes.string, | ||
font: PropTypes.string, | ||
color: PropTypes.string, | ||
content: PropTypes.oneOfType([PropTypes.string, PropTypes.array]), | ||
rotate: PropTypes.number, | ||
zIndex: PropTypes.number, | ||
logo: PropTypes.string, | ||
grayLogo: PropTypes.bool, | ||
isAutoWrap: PropTypes.bool, | ||
textOverflowEffect: PropTypes.oneOfType(['zoom', 'cut']) | ||
} | ||
|
||
Watermark.defaultProps = { | ||
id: null, | ||
density: 'default', | ||
textAlign: 'left', | ||
font: '16px microsoft yahei', | ||
color: 'rgba(128, 128, 128, 0.2)', | ||
content: '请勿外传', | ||
rotate: -30, | ||
zIndex: 1000, | ||
logo: null, | ||
grayLogo: true, | ||
isAutoWrap: false, | ||
textOverflowEffect: 'zoom' | ||
} | ||
Watermark.generate = WaterMarker | ||
export default Watermark |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
const defaultOptions = { | ||
id: null, | ||
textAlign: 'left', | ||
font: '16px microsoft yahei', | ||
color: 'rgba(128, 128, 128, 0.2)', | ||
content: '请勿外传', | ||
rotate: -30, | ||
zIndex: 1000, | ||
logo: null, | ||
grayLogo: true, // 是否对图标进行灰度处理 | ||
isAutoWrap: false, // 文字是否自动换行 | ||
textOverflowEffect: 'zoom' // 当isAutoWrap 为 false 时,文本长度超出画布长度时的处理方式: zoom - 缩小文字 cut - 截断文字 | ||
} | ||
|
||
const parseTextData = (ctx, texts, width, isWrap) => { | ||
let content = [] | ||
let lines = [] | ||
if (texts instanceof Array) { | ||
content = texts | ||
} else if (typeof texts === 'string') { | ||
content.push(texts) | ||
} else { | ||
console.warn('Only support String Or Array') | ||
} | ||
if (isWrap) { | ||
content.forEach((text) => { | ||
let curLine = '' | ||
for (let char of text) { | ||
let nextLine = curLine + char | ||
if (char === '\n' || ctx.measureText(nextLine).width > width) { | ||
lines.push(curLine) | ||
curLine = char === '\n' ? '' : char | ||
} else { | ||
curLine = nextLine | ||
} | ||
} | ||
lines.push(curLine) | ||
}) | ||
return lines | ||
} else { | ||
return content | ||
} | ||
} | ||
const drawText = (ctx, options) => { | ||
let { | ||
width, | ||
_w = width, | ||
height, | ||
textOverflowEffect, | ||
content: text, | ||
isAutoWrap, | ||
logo | ||
} = options | ||
let oldBaseLine = ctx.textBaseline | ||
let x = 0 | ||
let y = 0 | ||
ctx.textBaseline = 'hanging' | ||
/** | ||
* LOGO 固定宽高: 32 * 32 | ||
* 内容区域为 画布宽度 - 48 (预留左右各24的 padding) | ||
* 如含 LOGO ,文字的起始 X 坐标为: 24(padding-left) + 32(logo size) + 4(logo 与 text 间距) | ||
*/ | ||
let lineHeight = parseInt(ctx.font) // ctx.font必须以'XXpx'开头 | ||
if (logo) { | ||
x += 32 | ||
_w -= 32 | ||
} | ||
const lines = parseTextData(ctx, text, width, isAutoWrap) | ||
|
||
// 计算 Y 的起始位置 | ||
let lineY = y + (ctx.canvas.height) / 2 - (lineHeight * lines.length) / 2 | ||
const initLineY = lineY | ||
for (let line of lines) { | ||
let lineX | ||
if (ctx.textAlign === 'center') { | ||
lineX = x + width / 2 + 4 | ||
} else if (ctx.textAlign === 'right') { | ||
lineX = x + width + 4 | ||
} else { | ||
lineX = x + 4 | ||
} | ||
if (textOverflowEffect === 'zoom') { | ||
const size = parseInt(Math.sqrt((_w * _w + height * height) / 2)) | ||
ctx.fillText(line, lineX, lineY, size) | ||
} else { | ||
ctx.fillText(line, lineX, lineY) | ||
} | ||
lineY += lineHeight | ||
} | ||
ctx.textBaseline = oldBaseLine | ||
return initLineY - lineHeight / 2 | ||
} | ||
const drawLogo = (ctx, logo, cb) => { | ||
const img = new window.Image() | ||
img.src = logo | ||
img.onload = () => { | ||
ctx.globalAlpha = 0.2 | ||
ctx.drawImage(img, 0, ctx.canvas.height / 2 - 16, 32, 32) | ||
cb() | ||
} | ||
} | ||
|
||
const toImage = (canvas, key, container, options) => { | ||
var base64Url = canvas.toDataURL() | ||
const _top = (options._top || 0) + 'px' | ||
const __wm = document.querySelector(`.${key}`) | ||
const watermarkDiv = __wm || document.createElement('div') | ||
const styleStr = ` | ||
position:absolute; | ||
top:${_top}; | ||
left:0; | ||
bottom:0; | ||
width:100%; | ||
height:100%; | ||
z-index:${options.zIndex}; | ||
pointer-events:none; | ||
background-repeat:repeat; | ||
visibility:visible !important; | ||
display: block !important; | ||
opacity: 1 !important; | ||
background-image:url('${base64Url}'); | ||
${options.grayLogo ? '-webkit-filter: grayscale(100%);-moz-filter: grayscale(100%);-ms-filter: grayscale(100%);-o-filter: grayscale(100%);filter:progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);_filter:none;' : ''} | ||
` | ||
watermarkDiv.setAttribute('style', styleStr) | ||
if (window.getComputedStyle(container).getPropertyValue('position') === 'static') { | ||
container.style.position = 'relative' | ||
} | ||
watermarkDiv.classList.add(key) | ||
container.insertBefore(watermarkDiv, container.firstChild) | ||
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver | ||
if (MutationObserver) { | ||
let mo = new MutationObserver(function () { | ||
const __wm = document.querySelector(`.${key}`) | ||
const cs = __wm ? window.getComputedStyle(__wm) : {} | ||
if ((__wm && (__wm.getAttribute('style') !== styleStr || cs.visibility === 'hidden' || cs.display === 'none' || cs.opacity === 0)) || !__wm) { | ||
mo.disconnect() | ||
mo = null | ||
WaterMarker(container, JSON.parse(JSON.stringify(options))) | ||
} | ||
}) | ||
mo.observe(container, { | ||
attributes: true, | ||
subtree: true, | ||
childList: true | ||
}) | ||
} | ||
} | ||
const WaterMarker = (container, args) => { | ||
const _container = container || document.body | ||
const {density} = args | ||
let _markSize = { | ||
width: 210, | ||
height: 180 | ||
} | ||
if (['low', 'high'].includes(density)) { | ||
_markSize = { | ||
width: density === 'low' ? 240 : 180, | ||
height: density === 'low' ? 210 : 150 | ||
} | ||
} | ||
const options = Object.assign({}, defaultOptions, _markSize, args) | ||
const { | ||
id, | ||
width, | ||
height, | ||
textAlign, | ||
textBaseline, | ||
font, | ||
color, | ||
logo, | ||
rotate | ||
} = options | ||
let key = 'hi-' + Math.floor(Math.random() * (9999 - 1000)) + 1000 + '__wm' | ||
if (id && id.trim().length > 0 && !document.querySelector(id + '__wm')) { | ||
key = id + '__wm' | ||
} | ||
const canvas = document.createElement('canvas') | ||
var ctx = canvas.getContext('2d') | ||
canvas.setAttribute('width', width + 'px') | ||
canvas.setAttribute('height', height + 'px') | ||
|
||
ctx.textAlign = textAlign | ||
ctx.textBaseline = textBaseline | ||
ctx.font = font | ||
ctx.fillStyle = color | ||
ctx.translate(width / 2, height / 2) | ||
ctx.rotate(Math.PI / 180 * rotate) | ||
ctx.translate(-width / 2, -height / 2) | ||
|
||
drawText(ctx, options) | ||
if (logo) { | ||
drawLogo(ctx, logo, () => { | ||
toImage(canvas, key, _container, options) | ||
}) | ||
} else { | ||
toImage(canvas, key, _container, options) | ||
} | ||
} | ||
|
||
export default WaterMarker |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import React from 'react' | ||
import DocViewer from '../../../libs/doc-viewer' | ||
import Watermark from '../../../components/watermark' | ||
import logo from '../../../site/static/img/logo.png' | ||
const prefix = 'watermark-base' | ||
const desc = '' | ||
const code = `import React from 'react' | ||
import Watermark from '@hi-ui/hiui/es/Watermark'\n | ||
class Demo extends React.Component { | ||
constructor(props) { | ||
super(props) | ||
this.options = {logo: logo, content: ['HIUI', '做中台,就用 HIUI'],density:'high'} | ||
} | ||
render () { | ||
return ( | ||
<Watermark | ||
{...this.options} | ||
> | ||
<div id="watermark-box" | ||
style={{width: '100%', height: 400, border: '1px solid rgb(230, 231, 232)'}} ref={this.boxRef1} | ||
/> | ||
</Watermark> | ||
) | ||
} | ||
}` | ||
|
||
const DemoBase = () => ( | ||
<DocViewer | ||
desc={desc} | ||
code={code} | ||
scope={{ Watermark, logo }} | ||
prefix={prefix} | ||
/>) | ||
export default DemoBase |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Watermark 水印 | ||
|
||
用于向元素添加水印 | ||
|
||
## 何时使用 | ||
|
||
页面内容涉及到保密或其它情况时 | ||
|
||
## 基础用法 | ||
|
||
import DemoBase from '../../demo/watermark/section-base.jsx' | ||
|
||
<DemoBase /> | ||
|
||
## Props | ||
|
||
| 参数 | 说明 | 类型 | 可选值 | 默认值 | | ||
| ------- | ------------------------------------------ | ----------------------- | ---------------------------- | ---------- | | ||
| density | 水印间距,调节疏密程度 | string | 'low' \| 'default' \| 'high' | 'default' | | ||
| content | 水印文案,传入数组代表多行,不建议超过三行 | string \| Array[string] | - | '请勿外传' | | ||
| logo | 图片资源地址(暂只支持本地资源) | string | - | - | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.