mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-17 22:08:25 +08:00
354 lines
9.4 KiB
JavaScript
354 lines
9.4 KiB
JavaScript
// 将以空格分隔的字符串值转换成成数字/单位/值数组
|
||
const getNumberValueFromStr = value => {
|
||
let arr = String(value).split(/\s+/)
|
||
return arr.map(item => {
|
||
if (/^[\d.]+/.test(item)) {
|
||
// 数字+单位
|
||
let res = /^([\d.]+)(.*)$/.exec(item)
|
||
return [Number(res[1]), res[2]]
|
||
} else {
|
||
// 单个值
|
||
return item
|
||
}
|
||
})
|
||
}
|
||
|
||
// 缩放宽度
|
||
const zoomWidth = (ratio, height) => {
|
||
// w / height = ratio
|
||
return ratio * height
|
||
}
|
||
|
||
// 缩放高度
|
||
const zoomHeight = (ratio, width) => {
|
||
// width / h = ratio
|
||
return width / ratio
|
||
}
|
||
|
||
// 关键词到百分比值的映射
|
||
const keyWordToPercentageMap = {
|
||
left: 0,
|
||
top: 0,
|
||
center: 50,
|
||
bottom: 100,
|
||
right: 100
|
||
}
|
||
|
||
// 模拟background-size
|
||
const handleBackgroundSize = ({
|
||
backgroundSize,
|
||
drawOpt,
|
||
imageRatio,
|
||
canvasWidth,
|
||
canvasHeight,
|
||
canvasRatio
|
||
}) => {
|
||
if (backgroundSize) {
|
||
// 将值转换成数组
|
||
let backgroundSizeValueArr = getNumberValueFromStr(backgroundSize)
|
||
// 两个值都为auto,那就相当于不设置
|
||
if (
|
||
backgroundSizeValueArr[0] === 'auto' &&
|
||
backgroundSizeValueArr[1] === 'auto'
|
||
) {
|
||
return
|
||
}
|
||
// 值为cover
|
||
if (backgroundSizeValueArr[0] === 'cover') {
|
||
if (imageRatio > canvasRatio) {
|
||
// 图片的宽高比大于canvas的宽高比,那么图片高度缩放到和canvas的高度一致,宽度自适应
|
||
drawOpt.height = canvasHeight
|
||
drawOpt.width = zoomWidth(imageRatio, canvasHeight)
|
||
} else {
|
||
// 否则图片宽度缩放到和canvas的宽度一致,高度自适应
|
||
drawOpt.width = canvasWidth
|
||
drawOpt.height = zoomHeight(imageRatio, canvasWidth)
|
||
}
|
||
return
|
||
}
|
||
// 值为contain
|
||
if (backgroundSizeValueArr[0] === 'contain') {
|
||
if (imageRatio > canvasRatio) {
|
||
// 图片的宽高比大于canvas的宽高比,那么图片宽度缩放到和canvas的宽度一致,高度自适应
|
||
drawOpt.width = canvasWidth
|
||
drawOpt.height = zoomHeight(imageRatio, canvasWidth)
|
||
} else {
|
||
// 否则图片高度缩放到和canvas的高度一致,宽度自适应
|
||
drawOpt.height = canvasHeight
|
||
drawOpt.width = zoomWidth(imageRatio, canvasHeight)
|
||
}
|
||
return
|
||
}
|
||
// 图片宽度
|
||
let newNumberWidth = -1
|
||
if (backgroundSizeValueArr[0]) {
|
||
if (Array.isArray(backgroundSizeValueArr[0])) {
|
||
// 数字+单位类型
|
||
if (backgroundSizeValueArr[0][1] === '%') {
|
||
// %单位
|
||
drawOpt.width = (backgroundSizeValueArr[0][0] / 100) * canvasWidth
|
||
newNumberWidth = drawOpt.width
|
||
} else {
|
||
// 其他都认为是px单位
|
||
drawOpt.width = backgroundSizeValueArr[0][0]
|
||
newNumberWidth = backgroundSizeValueArr[0][0]
|
||
}
|
||
} else if (backgroundSizeValueArr[0] === 'auto') {
|
||
// auto类型,那么根据设置的新高度以图片原宽高比进行自适应
|
||
if (backgroundSizeValueArr[1]) {
|
||
if (backgroundSizeValueArr[1][1] === '%') {
|
||
// 高度为%单位
|
||
drawOpt.width = zoomWidth(
|
||
imageRatio,
|
||
(backgroundSizeValueArr[1][0] / 100) * canvasHeight
|
||
)
|
||
} else {
|
||
// 其他都认为是px单位
|
||
drawOpt.width = zoomWidth(imageRatio, backgroundSizeValueArr[1][0])
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// 设置了图片高度
|
||
if (backgroundSizeValueArr[1] && Array.isArray(backgroundSizeValueArr[1])) {
|
||
// 数字+单位类型
|
||
if (backgroundSizeValueArr[1][1] === '%') {
|
||
// 高度为%单位
|
||
drawOpt.height = (backgroundSizeValueArr[1][0] / 100) * canvasHeight
|
||
} else {
|
||
// 其他都认为是px单位
|
||
drawOpt.height = backgroundSizeValueArr[1][0]
|
||
}
|
||
} else if (newNumberWidth !== -1) {
|
||
// 没有设置图片高度或者设置为auto,那么根据设置的新宽度以图片原宽高比进行自适应
|
||
drawOpt.height = zoomHeight(imageRatio, newNumberWidth)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 模拟background-position
|
||
const handleBackgroundPosition = ({
|
||
backgroundPosition,
|
||
drawOpt,
|
||
imgWidth,
|
||
imgHeight,
|
||
canvasWidth,
|
||
canvasHeight
|
||
}) => {
|
||
if (backgroundPosition) {
|
||
// 将值转换成数组
|
||
let backgroundPositionValueArr = getNumberValueFromStr(backgroundPosition)
|
||
// 将关键词转为百分比
|
||
backgroundPositionValueArr = backgroundPositionValueArr.map(item => {
|
||
if (typeof item === 'string') {
|
||
return keyWordToPercentageMap[item] !== undefined
|
||
? [keyWordToPercentageMap[item], '%']
|
||
: item
|
||
}
|
||
return item
|
||
})
|
||
if (Array.isArray(backgroundPositionValueArr[0])) {
|
||
if (backgroundPositionValueArr.length === 1) {
|
||
// 如果只设置了一个值,第二个默认为50%
|
||
backgroundPositionValueArr.push([50, '%'])
|
||
}
|
||
// 水平位置
|
||
if (backgroundPositionValueArr[0][1] === '%') {
|
||
// 单位为%
|
||
let canvasX = (backgroundPositionValueArr[0][0] / 100) * canvasWidth
|
||
let imgX = (backgroundPositionValueArr[0][0] / 100) * imgWidth
|
||
// 计算差值
|
||
drawOpt.x = canvasX - imgX
|
||
} else {
|
||
// 其他单位默认都为px
|
||
drawOpt.x = backgroundPositionValueArr[0][0]
|
||
}
|
||
// 垂直位置
|
||
if (backgroundPositionValueArr[1][1] === '%') {
|
||
// 单位为%
|
||
let canvasY = (backgroundPositionValueArr[1][0] / 100) * canvasHeight
|
||
let imgY = (backgroundPositionValueArr[1][0] / 100) * imgHeight
|
||
// 计算差值
|
||
drawOpt.y = canvasY - imgY
|
||
} else {
|
||
// 其他单位默认都为px
|
||
drawOpt.y = backgroundPositionValueArr[1][0]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 模拟background-repeat
|
||
const handleBackgroundRepeat = ({
|
||
ctx,
|
||
image,
|
||
backgroundRepeat,
|
||
drawOpt,
|
||
imgWidth,
|
||
imgHeight,
|
||
canvasWidth,
|
||
canvasHeight
|
||
}) => {
|
||
if (backgroundRepeat) {
|
||
// 保存在handleBackgroundPosition中计算出来的x、y
|
||
let ox = drawOpt.x
|
||
let oy = drawOpt.y
|
||
// 计算ox和oy能平铺的图片数量
|
||
let oxRepeatNum = Math.ceil(ox / imgWidth)
|
||
let oyRepeatNum = Math.ceil(oy / imgHeight)
|
||
// 计算ox和oy第一张图片的位置
|
||
let oxRepeatX = ox - oxRepeatNum * imgWidth
|
||
let oxRepeatY = oy - oyRepeatNum * imgHeight
|
||
// 将值转换成数组
|
||
let backgroundRepeatValueArr = getNumberValueFromStr(backgroundRepeat)
|
||
// 不处理
|
||
if (
|
||
backgroundRepeatValueArr[0] === 'no-repeat' ||
|
||
(imgWidth >= canvasWidth && imgHeight >= canvasHeight)
|
||
) {
|
||
return
|
||
}
|
||
// 水平平铺
|
||
if (backgroundRepeatValueArr[0] === 'repeat-x') {
|
||
if (canvasWidth > imgWidth) {
|
||
let x = oxRepeatX
|
||
while (x < canvasWidth) {
|
||
drawImage(ctx, image, {
|
||
...drawOpt,
|
||
x
|
||
})
|
||
x += imgWidth
|
||
}
|
||
return true
|
||
}
|
||
}
|
||
// 垂直平铺
|
||
if (backgroundRepeatValueArr[0] === 'repeat-y') {
|
||
if (canvasHeight > imgHeight) {
|
||
let y = oxRepeatY
|
||
while (y < canvasHeight) {
|
||
drawImage(ctx, image, {
|
||
...drawOpt,
|
||
y
|
||
})
|
||
y += imgHeight
|
||
}
|
||
return true
|
||
}
|
||
}
|
||
// 平铺
|
||
if (backgroundRepeatValueArr[0] === 'repeat') {
|
||
let x = oxRepeatX
|
||
while (x < canvasWidth) {
|
||
if (canvasHeight > imgHeight) {
|
||
let y = oxRepeatY
|
||
while (y < canvasHeight) {
|
||
drawImage(ctx, image, {
|
||
...drawOpt,
|
||
x,
|
||
y
|
||
})
|
||
y += imgHeight
|
||
}
|
||
}
|
||
x += imgWidth
|
||
}
|
||
return true
|
||
}
|
||
}
|
||
}
|
||
|
||
// 根据参数绘制图片
|
||
const drawImage = (ctx, image, drawOpt) => {
|
||
ctx.drawImage(
|
||
image,
|
||
drawOpt.sx,
|
||
drawOpt.sy,
|
||
drawOpt.swidth,
|
||
drawOpt.sheight,
|
||
drawOpt.x,
|
||
drawOpt.y,
|
||
drawOpt.width,
|
||
drawOpt.height
|
||
)
|
||
}
|
||
|
||
const drawBackgroundImageToCanvas = (
|
||
ctx,
|
||
width,
|
||
height,
|
||
img,
|
||
{ backgroundSize, backgroundPosition, backgroundRepeat },
|
||
callback = () => {}
|
||
) => {
|
||
// 画布的长宽比
|
||
let canvasRatio = width / height
|
||
// 加载图片
|
||
let image = new Image()
|
||
image.src = img
|
||
image.onload = () => {
|
||
// 图片的宽度及长宽比
|
||
let imgWidth = image.width
|
||
let imgHeight = image.height
|
||
let imageRatio = imgWidth / imgHeight
|
||
// 绘制图片
|
||
// drawImage方法的参数值
|
||
let drawOpt = {
|
||
sx: 0,
|
||
sy: 0,
|
||
swidth: imgWidth,
|
||
sheight: imgHeight,
|
||
x: 0,
|
||
y: 0,
|
||
width: imgWidth,
|
||
height: imgHeight
|
||
}
|
||
// 模拟background-size
|
||
handleBackgroundSize({
|
||
backgroundSize,
|
||
drawOpt,
|
||
imageRatio,
|
||
canvasWidth: width,
|
||
canvasHeight: height,
|
||
canvasRatio
|
||
})
|
||
|
||
// 模拟background-position
|
||
handleBackgroundPosition({
|
||
backgroundPosition,
|
||
drawOpt,
|
||
imgWidth: drawOpt.width,
|
||
imgHeight: drawOpt.height,
|
||
imageRatio,
|
||
canvasWidth: width,
|
||
canvasHeight: height,
|
||
canvasRatio
|
||
})
|
||
|
||
// 模拟background-repeat
|
||
let notNeedDraw = handleBackgroundRepeat({
|
||
ctx,
|
||
image,
|
||
backgroundRepeat,
|
||
drawOpt,
|
||
imgWidth: drawOpt.width,
|
||
imgHeight: drawOpt.height,
|
||
imageRatio,
|
||
canvasWidth: width,
|
||
canvasHeight: height,
|
||
canvasRatio
|
||
})
|
||
|
||
// 绘制图片
|
||
if (!notNeedDraw) {
|
||
drawImage(ctx, image, drawOpt)
|
||
}
|
||
|
||
callback()
|
||
}
|
||
image.onerror = e => {
|
||
callback(e)
|
||
}
|
||
}
|
||
|
||
export default drawBackgroundImageToCanvas |