UNPKG

leo-mind-map

Version:

一个简单的web在线思维导图

355 lines (341 loc) 9.59 kB
// 将以空格分隔的字符串值转换成成数字/单位/值数组 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