sh-tools 1.2.2 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,311 @@
1
+ import XEUtils from 'xe-utils'
2
+
3
+ export default {
4
+ toDateDiffText(date) {
5
+ let currDate = 1544407800000 // '2018-12-10 10:10:00'
6
+ let dateDiff = XEUtils.getDateDiff(date, currDate)
7
+ if (dateDiff.done) {
8
+ if (dateDiff.time < 31536000000) {
9
+ if (dateDiff.time < 2592000000) {
10
+ if (dateDiff.time < 86400000) {
11
+ if (dateDiff.time < 360000) {
12
+ if (dateDiff.time < 60000) {
13
+ if (dateDiff.time < 10000) {
14
+ return '刚刚'
15
+ }
16
+ return `${dateDiff.ss}秒之前`
17
+ }
18
+ return `${dateDiff.mm}分钟之前`
19
+ }
20
+ return `${dateDiff.HH}小时之前`
21
+ }
22
+ return `${dateDiff.dd}天之前`
23
+ }
24
+ return `${dateDiff.MM}个月之前`
25
+ }
26
+ return `${dateDiff.yyyy}年之前`
27
+ }
28
+ return '错误类型'
29
+ },
30
+ ioToFile(io, fileName = 'download', type = 'blob') {
31
+ return new Promise((resolve, reject) => {
32
+ try {
33
+ if ('download' in document.createElement('a')) {
34
+ // 非IE下载
35
+ const elink = document.createElement('a')
36
+ const url = window.URL || window.webkitURL || window.moxURL
37
+ elink.style.display = 'none'
38
+ elink.href = url.createObjectURL(io)
39
+ elink.download = fileName
40
+ elink.click()
41
+ url.revokeObjectURL(elink.href)
42
+ } else {
43
+ // IE10+下载
44
+ navigator.msSaveBlob(io, fileName)
45
+ }
46
+ resolve()
47
+ } catch (e) {
48
+ reject(e)
49
+ }
50
+ })
51
+ },
52
+ // 数字金额转大写
53
+ toMoneyChies(val, option = {}) {
54
+ let { upcase = true } = option
55
+ //汉字的数字
56
+ var cnNums = upcase ? ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'] : ['零', '一', '二', '三', '四', '伍', '六', '七', '八', '九']
57
+ //基本单位
58
+ var cnIntRadice = ['', '拾', '佰', '仟']
59
+ //对应整数部分扩展单位
60
+ var cnIntUnits = ['', '万', '亿', '兆']
61
+ //对应小数部分单位
62
+ var cnDecUnits = ['角', '分', '毫', '厘']
63
+ //整数金额时后面跟的字符
64
+ var cnInteger = '整'
65
+ //整型完以后的单位
66
+ var cnIntLast = '元'
67
+ //最大处理的数字
68
+ var maxNum = 999999999999999.9999
69
+ //金额整数部分
70
+ var integerNum
71
+ //金额小数部分
72
+ var decimalNum
73
+ //输出的中文金额字符串
74
+ var chineseStr = ''
75
+ //分离金额后用的数组,预定义
76
+ var parts
77
+ if (val === '') return val
78
+ val = parseFloat(val)
79
+ if (val >= maxNum) {
80
+ //超出最大处理数字
81
+ return ''
82
+ }
83
+ if (val === 0) {
84
+ chineseStr = cnNums[0] + cnIntLast + cnInteger
85
+ return chineseStr
86
+ }
87
+ //转换为字符串
88
+ val = val.toString()
89
+ if (val.indexOf('.') === -1) {
90
+ integerNum = val
91
+ decimalNum = ''
92
+ } else {
93
+ parts = val.split('.')
94
+ integerNum = parts[0]
95
+ decimalNum = parts[1].substr(0, 4)
96
+ }
97
+ //获取整型部分转换
98
+ if (parseInt(integerNum, 10) > 0) {
99
+ var zeroCount = 0
100
+ var IntLen = integerNum.length
101
+ for (let i = 0; i < IntLen; i++) {
102
+ let n = integerNum.substr(i, 1)
103
+ var p = IntLen - i - 1
104
+ var q = p / 4
105
+ var m = p % 4
106
+ if (n === '0') {
107
+ zeroCount++
108
+ } else {
109
+ if (zeroCount > 0) {
110
+ chineseStr += cnNums[0]
111
+ }
112
+ //归零
113
+ zeroCount = 0
114
+ chineseStr += cnNums[parseInt(n)] + cnIntRadice[m]
115
+ }
116
+ if (m === 0 && zeroCount < 4) {
117
+ chineseStr += cnIntUnits[q]
118
+ }
119
+ }
120
+ chineseStr += cnIntLast
121
+ }
122
+ //小数部分
123
+ if (decimalNum !== '') {
124
+ var decLen = decimalNum.length
125
+ for (var i = 0; i < decLen; i++) {
126
+ var n = decimalNum.substr(i, 1)
127
+ if (n !== '0') {
128
+ chineseStr += cnNums[Number(n)] + cnDecUnits[i]
129
+ }
130
+ }
131
+ }
132
+ if (chineseStr === '') {
133
+ chineseStr += cnNums[0] + cnIntLast + cnInteger
134
+ } else if (decimalNum === '') {
135
+ chineseStr += cnInteger
136
+ }
137
+ return chineseStr
138
+ },
139
+ // 补全金额位数,返回字符串
140
+ formatMoneyPad(value) {
141
+ //基本单位
142
+ let cnIntRadice = ['亿', '千', '百', '十', '万', '千', '百', '十', '元']
143
+ // 小数单位
144
+ let cnDecUnits = ['角', '分', '毫', '厘']
145
+ let intValue = String(value).split('.')[0] || ''
146
+ let decValue = String(value).split('.')[1] || ''
147
+ let intFixed = intValue.padStart(cnIntRadice.length, ' ')
148
+ let decFixed = decValue.padEnd(cnDecUnits.length, '0')
149
+ return intFixed + '.' + decFixed
150
+ },
151
+ // 封装 系统内对渲染器格式化方法
152
+ formatRender(value, key, rowData, renderName, renderProps, renderContext, editable) {
153
+ // editable: 是否对返回值进行修正 默认 否
154
+ let rvalue, rtext, rname, iscross
155
+ let { $vUtils, $vxePluginNames, rform } = renderContext
156
+ let { min, max, formula, digits, type, multiple, split, options, format, nodeKey, moneyUnit, commafy, openValue, openLabel, closeValue, closeLabel, bill } = renderProps
157
+ let defaultDateFormat = {
158
+ date: 'yyyy-MM-dd',
159
+ time: 'HH:mm:ss',
160
+ datetime: 'yyyy-MM-dd HH:mm:ss',
161
+ month: 'yyyy-MM',
162
+ week: 'yyyy-MM-dd',
163
+ year: 'yyyy'
164
+ }
165
+ if (renderName === '$vRowCell') {
166
+ let field = rowData[key + 'field'] || key
167
+ rname = rowData[field + 'RenderName'] || '$vInput'
168
+ } else {
169
+ rname = renderName
170
+ }
171
+ switch (rname) {
172
+ case '$vInput':
173
+ if (formula) {
174
+ value = $vUtils.calculate(formula, rowData)
175
+ }
176
+ if (editable && ['integer', 'number', 'float'].includes(type) && !$vUtils.isNone(value)) {
177
+ if ($vUtils.isNumber(+max) && +value > +max) {
178
+ value = +max
179
+ iscross = true
180
+ } else if ($vUtils.isNumber(+min) && +value < +min) {
181
+ value = +min
182
+ iscross = true
183
+ }
184
+ }
185
+ switch (type) {
186
+ case 'integer':
187
+ rvalue = value || value === 0 ? $vUtils.toInteger(value) : ''
188
+ break
189
+ case 'number':
190
+ case 'float':
191
+ rvalue = value || value === 0 ? $vUtils.truncate(value, digits) : ''
192
+ break
193
+ default:
194
+ rvalue = value
195
+ break
196
+ }
197
+ rvalue = $vUtils.replaceNutrim(rvalue)
198
+ rtext = rvalue
199
+ break
200
+ case '$vTime':
201
+ format = format ? format.replace('YYYY', 'yyyy').replace('DD', 'dd').replace('hh', 'HH') : defaultDateFormat[type]
202
+ if ($vUtils.isNumber(value)) value = String(value)
203
+ rvalue = format ? (value ? $vUtils.toDateString(value, format) : '') : value
204
+ rvalue = $vUtils.replaceNutrim(rvalue)
205
+ if ($vUtils.isDate(value)) $vUtils.set(rowData, key, rvalue)
206
+ rtext = rvalue
207
+ break
208
+ case '$vTextArea':
209
+ case '$vCheckbox':
210
+ case '$vRadio':
211
+ case '$vHref':
212
+ rvalue = $vUtils.replaceNutrim(value)
213
+ rtext = rvalue
214
+ break
215
+ case '$vSelect':
216
+ if (multiple) {
217
+ let oriArray = Array.isArray(value) ? value : value ? value.split(split).filter(_ => _ && _ !== 'undefined') : []
218
+ let rns = oriArray.map(orv => {
219
+ return options.find(opt => String(opt.value) === String(orv)).label || ''
220
+ })
221
+ rtext = rns.join(split)
222
+ rvalue = oriArray
223
+ } else {
224
+ let optionObj = options.find(_ => String(_.value) === String(value))
225
+ rtext = optionObj ? optionObj.label : ''
226
+ rvalue = value
227
+ }
228
+ break
229
+ case '$vTree':
230
+ let prefixKey = key.endsWith('Id') ? String(key).replace('Id', '') : key
231
+ if (multiple) {
232
+ let oriArray = Array.isArray(value) ? value : value ? value.split(split).filter(_ => _ && _ !== 'undefined') : []
233
+ if (options && options.length > 0) {
234
+ let rns = oriArray.map(orv => options.find(opt => String(opt[nodeKey]) === String(orv)).label || '')
235
+ rtext = rns.join(split)
236
+ }
237
+ rvalue = oriArray
238
+ } else {
239
+ if (options && options.length > 0) {
240
+ let optionObj = options.find(opt => String(opt[nodeKey]) === String(value))
241
+ rtext = optionObj ? optionObj.label : ''
242
+ }
243
+ rvalue = value ? [value] : []
244
+ }
245
+ if (format) {
246
+ let dataformat = format.replace(/@/gi, prefixKey)
247
+ rtext = $vUtils.format(dataformat, rowData)
248
+ }
249
+ break
250
+ case '$vProgress':
251
+ rvalue = value || value === 0 ? $vUtils.truncate(value, digits) : ''
252
+ rtext = rvalue
253
+ break
254
+ case '$vSwitch':
255
+ rvalue = $vUtils.replaceNutrim(value)
256
+ if (String(rvalue) === openValue) {
257
+ rtext = openLabel
258
+ } else if (String(rvalue) === closeValue) {
259
+ rtext = closeLabel
260
+ }
261
+ break
262
+ case '$vMoney':
263
+ if (formula) {
264
+ value = $vUtils.calculate(formula, rowData)
265
+ }
266
+ if (editable && !$vUtils.isNone(value)) {
267
+ if ($vUtils.isNumber(+max) && +value > +max) {
268
+ value = +max
269
+ iscross = true
270
+ } else if ($vUtils.isNumber(+min) && +value < +min) {
271
+ value = +min
272
+ iscross = true
273
+ }
274
+ }
275
+ rvalue = value || value === 0 ? (type !== 'integer' ? $vUtils.truncate(value, digits) : $vUtils.toInteger(value)) : ''
276
+ rvalue = $vUtils.replaceNutrim(rvalue)
277
+ rtext = rvalue !== '' ? $vUtils.truncate($vUtils.divide(rvalue, moneyUnit), digits) : ''
278
+ if (commafy && !bill && bill !== '0') rtext = $vUtils.commafy(rtext, { digits })
279
+ break
280
+ case '$vCheckgroup':
281
+ let oriArray = Array.isArray(value) ? value : value ? value.split(split).filter(_ => _ && _ !== 'undefined') : []
282
+ let rns = oriArray.map(orv => {
283
+ return options.find(opt => String(opt.value) === String(orv)).label || ''
284
+ })
285
+ rtext = rns.join(split)
286
+ rvalue = oriArray
287
+ break
288
+ case '$vRadiogroup':
289
+ let optionObj = options.find(_ => String(_.value) === String(value))
290
+ rtext = optionObj ? optionObj.label : ''
291
+ rvalue = value
292
+ break
293
+ case '$vUpload':
294
+ rvalue = Array.isArray(value) ? value : value.split(split).filter(_ => _ && _ !== 'undefined')
295
+ break
296
+ case '$vCode':
297
+ rvalue = value
298
+ rtext = value
299
+ break
300
+ default:
301
+ break
302
+ }
303
+ if (formula) {
304
+ $vUtils.set(rowData, key, rvalue)
305
+ }
306
+ // if (!rform && $vxePluginNames.expendRenders && $vxePluginNames.expendRenders.includes(renderName)) {
307
+ // $vUtils.set(rowData, `${key}${$vxePluginNames.expendName}`, rtext)
308
+ // }
309
+ return { rvalue, rtext }
310
+ }
311
+ }
@@ -0,0 +1,43 @@
1
+ export default {
2
+ numberReg() {
3
+ return {
4
+ number: /^[0-9]*$/, // 数字
5
+ numberX: /^\d{n}$/, // n位的数字
6
+ numberMinx: /^\d{n,}$/, // 至少n位的数字
7
+ numberMn: /^\d{m,n}$/, // m-n位的数字
8
+ numberFloat: /^(-)?\d+(\.\d{1,2})$/, // 带1-2位小数的正数或负数
9
+ numberC: /^(-|\+)?\d+(\.\d+)?$/, // 正数、负数、和小数
10
+ numberFloatZ: /^[0-9]+(\.[0-9]{2})?$/, // 有两位小数的正实数
11
+ numberZhengZ: /^\+?[1-9][0-9]*$/, // 非零的正整数
12
+ numberZhengF: /^-[1-9]\d*$/, // 非零的负整数
13
+ numberFix: /^(-?\d+)(\.\d+)?$/, // 浮点数
14
+ numberFixZ: /^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$/, // 正浮点数
15
+ numberFixF: /^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$/ // 负浮点数
16
+ }
17
+ },
18
+ stringReg() {
19
+ return {
20
+ stringChinese: /^[\u4e00-\u9fa5]{0,}$/i, // 汉字
21
+ stringEngNum: /^[A-Za-z0-9]+$/i, // 汉字
22
+ stringMn: /^.{3,20}$/i, // 长度为m-n的所有字符
23
+ stringEng: /^[A-Za-z]+$/i, // 字母
24
+ stringEngUp: /^[A-Z]+$/i, // 大写字母
25
+ stringEngLow: /^[a-z]+$/i // 小写字母
26
+ }
27
+ },
28
+ mainReg() {
29
+ return {
30
+ email: /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/, // 邮箱
31
+ www: /^[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/, // 域名
32
+ url: /^http[s]{0,1}:\/\/.+$/, // url
33
+ phone: /^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$/, // 手机号码
34
+ tel: /^([0-9]{3,4}-)?[0-9]{7,8}$/, // 固定电话
35
+ telphone: /^(1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8})|(([0-9]{3,4}-)?[0-9]{7,8})$/, // 手机号码与固定电话
36
+ idcard: /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/, // 身份证号
37
+ postcode: /^[1-9]\d{5}(?!\d)$/, // 邮编
38
+ qq: /^(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)$/, // qq
39
+ space: /(^\s*)|(\s*$)/g, // 前后空格
40
+ spaceAll: /^\s+|\s+$/g // 含有空格
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,227 @@
1
+ import XEUtils from 'xe-utils'
2
+
3
+ // 数据格式化获取keys
4
+ function getFormatKeys(format) {
5
+ let regR = new RegExp('({[a-zA-Z0-9_.]*})', 'ig')
6
+ let formatStr = String(format)
7
+ return (formatStr.match(regR) || []).map((key, keyIndex) => {
8
+ return key.replace(/{|}/gi, '')
9
+ })
10
+ }
11
+
12
+ // 格式化数据结构
13
+ function format(format, data) {
14
+ let keys = getFormatKeys(format)
15
+ let formatStr = String(format)
16
+ keys.map((key, indexkey) => {
17
+ let value = XEUtils.get(data, key) || ''
18
+ formatStr = formatStr.replace(new RegExp('{' + key + '}', 'ig'), value)
19
+ })
20
+ return formatStr
21
+ }
22
+
23
+ // 生成随机字符串
24
+ function randomStr(len = 32) {
25
+ const $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
26
+ const maxPos = $chars.length
27
+ let str = ''
28
+ for (let i = 0; i < len; i++) {
29
+ str += $chars.charAt(Math.floor(Math.random() * maxPos))
30
+ }
31
+ return str
32
+ }
33
+
34
+ export default {
35
+ randomStr,
36
+ getFormatKeys,
37
+ format,
38
+ replaceNutrim(str) {
39
+ return String(str).replace(/null|undefined|(^\s*)|(\s*$)/gi, '')
40
+ },
41
+ replaceAll(str, from, to) {
42
+ // 替换全部 将str中的所有from替换为to
43
+ return str.replace(new RegExp(from, 'gm'), to)
44
+ },
45
+ filterTag(str) {
46
+ /* 过滤html代码(把<>转换字符串) */
47
+ let me = this
48
+ str = me.replaceAll(str, /&/gi, '&amp;')
49
+ str = me.replaceAll(str, /</gi, '&lt;')
50
+ str = me.replaceAll(str, />/gi, '&gt;')
51
+ str = me.replaceAll(str, ' ', '&nbsp;')
52
+ return str
53
+ },
54
+ setWaterMark(text, settings) {
55
+ // 生成水印 推荐 canvas
56
+ class WaterMark {
57
+ constructor(text, settings) {
58
+ this.settings = Object.assign(
59
+ {
60
+ container: document.body,
61
+ id: 'waterMark',
62
+ showType: 'canvas', // div 还是 canvas
63
+ x: 0, // 水印起始位置x轴坐标
64
+ y: 100, // 水印起始位置Y轴坐标
65
+ xSpace: 150, // 水印x轴间隔
66
+ ySpace: 100, // 水印y轴间隔
67
+ color: '#cfcccc', // 水印字体颜色
68
+ alpha: 0.5, // 水印透明度
69
+ fontsize: '15px', // 水印字体大小
70
+ angle: 20, // 水印倾斜度数
71
+ curTime: false // 是否自动拼接当前时间
72
+ },
73
+ settings || {}
74
+ )
75
+ this.globalWidth = this.settings.container.clientWidth
76
+ this.globalHeight = this.settings.container.clientHeight
77
+ this.initCanvas()
78
+ this.draw(text)
79
+ this.insertWaterMark()
80
+ }
81
+ initCanvas() {
82
+ this.renderCanvas = document.createElement('canvas')
83
+ this.renderContext = this.renderCanvas.getContext('2d')
84
+ this.renderCanvas.width = this.globalWidth
85
+ this.renderCanvas.height = this.globalHeight
86
+ this.div = document.createElement('div')
87
+ this.oldData = this.renderContext.getImageData(0, 0, this.globalWidth, this.globalHeight)
88
+ }
89
+ insertWaterMark() {
90
+ const { type, id, alpha, container } = this.settings
91
+ this.WaterMarkEl = this.renderCanvas
92
+ if (type === 'div') {
93
+ this.WaterMarkEl = this.div
94
+ this.WaterMarkEl.style.backgroundImage = 'url(' + this.renderCanvas.toDataURL('image/png') + ')'
95
+ }
96
+ const style = this.WaterMarkEl.style
97
+ style.position = 'absolute'
98
+ style.opacity = alpha
99
+ style.top = style.left = 0
100
+ style.zIndex = '9999'
101
+ style.width = this.globalWidth + 'px'
102
+ style.height = this.globalHeight + 'px'
103
+ style.pointerEvents = 'none'
104
+ this.WaterMarkEl.id = id
105
+ container.append(this.WaterMarkEl)
106
+ }
107
+ draw(waterMarkText, settings) {
108
+ this.settings = Object.assign(this.settings, settings || {})
109
+ let { x, y, xSpace, ySpace, color, fontsize, angle, curTime } = this.settings
110
+ let { globalWidth, globalHeight, renderContext } = this
111
+ let timeStr = ''
112
+ if (curTime) {
113
+ let today = new Date()
114
+ timeStr =
115
+ today.getFullYear() +
116
+ '-' +
117
+ String(today.getMonth() + 1).padStart(2, 0) +
118
+ '-' +
119
+ String(today.getDate()).padStart(2, 0) +
120
+ ' ' +
121
+ String(today.getHours()).padStart(2, 0) +
122
+ ':' +
123
+ String(today.getMinutes()).padStart(2, 0) +
124
+ ':' +
125
+ String(today.getSeconds()).padStart(2, 0)
126
+ }
127
+ waterMarkText = waterMarkText ? waterMarkText + ' ' + timeStr : timeStr
128
+ renderContext.clearRect(0, 0, globalWidth, globalHeight)
129
+ renderContext.font = fontsize + ' microsoft yahei'
130
+ renderContext.fillStyle = color
131
+ renderContext.textAlign = 'left'
132
+ renderContext.textBaseline = 'Middle'
133
+ let measureText = renderContext.measureText(waterMarkText)
134
+ let width = measureText.width
135
+ let height = Math.ceil(width / Math.tan(angle) + 50)
136
+ for (let xx = x; xx < globalWidth; xx += xSpace + width) {
137
+ for (let yy = y; yy < globalHeight; yy += ySpace + height) {
138
+ // 填充文字,x 间距, y 间距
139
+ renderContext.save()
140
+ renderContext.translate(xx, yy)
141
+ renderContext.rotate(0 - (angle * Math.PI) / 180)
142
+ renderContext.fillText(waterMarkText, 0, 0)
143
+ renderContext.restore()
144
+ }
145
+ }
146
+ }
147
+ // 图片的像素信息里存储着 RGB 的色值,R、G、B 分别为该像素的红、绿、蓝通道,每个通道的分量值范围在 0~255,16 进制则是 00~FF。在 CSS 中经常使用其 16 进制形式,比如指定博客头部背景色为 #A9D5F4。其中 R(红色)的 16 进制值为 A9,换算成十进制为 169。这时候,对 R 分量的值 + 1,即为 170,整个像素 RGB 值为 #AAD5F4,别说你看不出差别,就连火眼金金的“ 像素眼” 设计师都察觉不出来呢。于此同时,修改 G、B 的分量值,也是我们无法察觉的。因此可以得出重要结论:RGB 分量值的小量变动,是肉眼无法分辨的,不影响对图片的识别。
148
+ // 有了这个结论,那就给我们了利用空间,常用手段的就是对二进制最低位进行操作,下面就用 canvas 来演示一下。
149
+ inVisible(ctx, newData, originalData, color = 'R') {
150
+ var oData = originalData.data
151
+ var bit, offset // offset的作用是找到alpha通道值,这里需要大家自己动动脑筋
152
+ switch (color) {
153
+ case 'R':
154
+ bit = 0
155
+ offset = 3
156
+ break
157
+ case 'G':
158
+ bit = 1
159
+ offset = 2
160
+ break
161
+ case 'B':
162
+ bit = 2
163
+ offset = 1
164
+ break
165
+ }
166
+ for (var i = 0; i < oData.length; i++) {
167
+ if (i % 4 === bit) {
168
+ // 只处理目标通道
169
+ if (newData[i + offset] === 0 && oData[i] % 2 === 1) {
170
+ // 没有信息的像素,该通道最低位置0,但不要越界
171
+ if (oData[i] === 255) {
172
+ oData[i]--
173
+ } else {
174
+ oData[i]++
175
+ }
176
+ } else if (newData[i + offset] !== 0 && oData[i] % 2 === 0) {
177
+ // // 有信息的像素,该通道最低位置1,可以想想上面的斑点效果是怎么实现的
178
+ oData[i]++
179
+ }
180
+ }
181
+ }
182
+ ctx.putImageData(originalData, 0, 0)
183
+ }
184
+ visible(ctx, originalData, color = 'R') {
185
+ var bit, offset // offset的作用是找到alpha通道值,这里需要大家自己动动脑筋
186
+ switch (color) {
187
+ case 'R':
188
+ bit = 0
189
+ offset = 3
190
+ break
191
+ case 'G':
192
+ bit = 1
193
+ offset = 2
194
+ break
195
+ case 'B':
196
+ bit = 2
197
+ offset = 1
198
+ break
199
+ }
200
+ var data = originalData.data
201
+ for (var i = 0; i < data.length; i++) {
202
+ if (i % 4 === bit) {
203
+ // 只处理目标通道
204
+ if (data[i] % 2 === 0) {
205
+ // 没有信息的像素,该通道最低位置0,但不要越界
206
+ if (data[i] % 2 === 0) {
207
+ data[i] = 0
208
+ } else if (i % 4 === 3) {
209
+ // alpha通道不做处理
210
+ continue
211
+ } else {
212
+ // 关闭其他分量,不关闭也不影响答案
213
+ data[i] = 0
214
+ }
215
+ }
216
+ }
217
+ ctx.putImageData(originalData, 0, 0)
218
+ }
219
+ }
220
+ }
221
+ return new WaterMark(text, settings)
222
+ },
223
+ removeWaterMark(container, id) {
224
+ const body = container || document.body
225
+ body.removeChild(document.getElementById(id || 'waterMark'))
226
+ }
227
+ }
@@ -0,0 +1 @@
1
+ export default {}