Comment simuler le style d'image d'arrière-plan de css dans canvas

L'auteur a mis en open source une mind-map web mind -map . Récemment, j'ai rencontré un problème lors de l'optimisation de l'effet de l'image d'arrière-plan. Lorsqu'elle est affichée sur la page, l'image d'arrière-plan est rendue à cssl'aide de , mais lorsqu'elle est exportée, elle est en fait dessiné vers le haut. Ensuite, il y aura un problème, l'image d'arrière-plan de l'image d'arrière-plan prend en charge des effets plus riches, tels que la définition de la taille, de la position et de la répétition, mais l'auteur n'a trouvé qu'une seule méthode et ne prend en charge que la définition de la répétition effet, alors comment simuler un certain En ce qui concerne l'effet de fond, ne partez pas, essayons-le ensemble ensuite.background-imagecanvascssbackground-sizebackground-positionbackground-repeatcanvascreatePattern()canvascss

La première chose à expliquer est qu'il ne 100%simulera pas cssparfaitement et complètement tous les effets, car il cssest trop puissant, la combinaison des valeurs d'attributs est très flexible, et il en existe de nombreux types, parmi lesquels il existe de nombreux types d'unités, ainsi, seules certaines situations courantes seront simulées et seules les unités seront prises en pxcompte %.

Après avoir lu cet article, vous pouvez également revoir la canvasméthode drawImageet cssl'utilisation de plusieurs attributs définis en arrière-plan d'ailleurs.

La méthode drawImage() de canvas

En général, nous utiliserons canvasla drawImage()méthode pour dessiner l'image de fond. Voyons d'abord cette méthode. Cette méthode reçoit plus de paramètres :

Seuls trois paramètres sont requis.

Cadre et outils de base

La logique de base consiste à charger l'image, puis à utiliser drawImagela méthode pour dessiner l'image, qui n'est rien de plus que des paramètres csscalculés en fonction de divers attributs et valeurs drawImage, de sorte que le cadre de base suivant de la fonction peut être écrit :

const drawBackgroundImageToCanvas = (
  ctx,// canvas绘图上下文
  width,// canvas宽度
  height,// canvas高度
  img,// 图片url
  {
    
     backgroundSize, backgroundPosition, backgroundRepeat }// css样式,只模拟这三种
) => {
    
    
  // canvas的宽高比
  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
    }
    // 根据css属性和值计算...
    // 绘制图片
    ctx.drawImage(image, drawOpt.sx, drawOpt.sy, drawOpt.swidth, drawOpt.sheight, drawOpt.x, drawOpt.y, drawOpt.width, drawOpt.height)
  }
}

Ensuite, regardons quelques fonctions de l'outil.

// 将以空格分隔的字符串值转换成成数字/单位/值数组
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
    }
  })
}

cssLa valeur d'attribut de est une chaîne ou un type numérique, par exemple 100px 100% auto, il n'est pas pratique de l'utiliser directement, il est donc converti en [[100, 'px'], [100, '%'], 'auto']formulaire.

// 缩放宽度
const zoomWidth = (ratio, height) => {
    
    
    // w / height = ratio
    return ratio * height
}

// 缩放高度
const zoomHeight = (ratio, width) => {
    
    
  // width / h = ratio
  return width / ratio
}

Calculez la largeur ou la hauteur mise à l'échelle en fonction du rapport d'origine et de la nouvelle largeur ou hauteur.

Simuler la propriété background-size

background-repeatLa valeur par défaut est repeat, nous ne considérons pas le cas de duplication, alors réglez-la sur no-repeat.

background-sizeL'attribut est utilisé pour définir la taille de l'image d'arrière-plan et peut accepter quatre types de valeurs, qui sont simulées à tour de rôle.

type de longueur

Définissez la hauteur et la largeur de l'image d'arrière-plan. La première valeur définit la largeur et la seconde définit la hauteur. Si une seule valeur est donnée, la seconde par défaut est auto (automatique).

cssLe style est le suivant :

.cssBox {
    
    
    background-image: url('/1.jpg');
    background-repeat: no-repeat;
    background-size: 300px;
}

Si une seule valeur est définie, elle représente la largeur réelle de l'affichage de l'image d'arrière-plan. Si la hauteur n'est pas définie, elle sera automatiquement mise à l'échelle en fonction du rapport d'aspect de l'image. L'effet est le suivant :

La simulation dans canvasest très simple, et quatre paramètres doivent être passés à drawImagela méthode : img、x、y、width、height, imgreprésentant l'image, x、yreprésentant la position de placement de l'image sur le canevas, il n'y a pas de réglage spécial, évidemment, 0、0cela width、heightreprésente le zoom de l'image à la valeur spécifiée taille, si background-sizeune seule valeur est transmise, alors widthréglez-la directement sur cette valeur, et heightcalculez en fonction du rapport d'aspect de l'image. Si deux valeurs sont transmises, alors transmettez les deux valeurs séparément width、height. De plus, vous avez besoin pour autotraiter la valeur, comme suit :

drawBackgroundImageToCanvas(ctx, width, height, this.img, {
    
    
    backgroundSize: '300px'
})

const drawBackgroundImageToCanvas = () =>{
    
    
    // ...
    image.onload = () => {
    
    
        // ...
        // 模拟background-size
        handleBackgroundSize({
    
    
            backgroundSize, 
            drawOpt, 
            imageRatio
        })
        // ...
    }
}

// 模拟background-size
const handleBackgroundSize = ({
     
      backgroundSize, drawOpt, imageRatio }) => {
    
    
    if (backgroundSize) {
    
    
      // 将值转换成数组
      let backgroundSizeValueArr = getNumberValueFromStr(backgroundSize)
      // 两个值都为auto,那就相当于不设置
      if (backgroundSizeValueArr[0] === 'auto' && backgroundSizeValueArr[1] === 'auto') {
    
    
        return
      }
      // 图片宽度
      let newNumberWidth = -1
      if (backgroundSizeValueArr[0]) {
    
    
        if (Array.isArray(backgroundSizeValueArr[0])) {
    
    
            // 数字+单位类型
            drawOpt.width = backgroundSizeValueArr[0][0]
            newNumberWidth = backgroundSizeValueArr[0][0]
        } else if (backgroundSizeValueArr[0] === 'auto') {
    
    
            // auto类型,那么根据设置的新高度以图片原宽高比进行自适应
            if (backgroundSizeValueArr[1]) {
    
    
                drawOpt.width = zoomWidth(imageRatio, backgroundSizeValueArr[1][0])
            }
        }
      }
      // 设置了图片高度
      if (backgroundSizeValueArr[1] && Array.isArray(backgroundSizeValueArr[1])) {
    
    
        // 数字+单位类型
        drawOpt.height = backgroundSizeValueArr[1][0]
      } else if (newNumberWidth !== -1) {
    
    
        // 没有设置图片高度或者设置为auto,那么根据设置的新宽度以图片原宽高比进行自适应
        drawOpt.height = zoomHeight(imageRatio, newNumberWidth)
      }
    }
}

L'effet est le suivant :

L'effet de la définition de deux valeurs :

background-size: 300px 400px;

type de pourcentage

Le pourcentage de la zone localisée par rapport à l'arrière-plan sera calculé. La première valeur définit le pourcentage de largeur, la seconde valeur définit le pourcentage de hauteur. Si une seule valeur est donnée, la seconde par défaut est auto (automatique). Par exemple, s'il est défini 50% 80%, cela signifie que l'image sera mise à l'échelle en fonction 50%de la largeur et 80%de la hauteur de la zone d'arrière-plan.

cssLe style est le suivant :

.cssBox {
    
    
    background-image: url('/1.jpg');
    background-repeat: no-repeat;
    background-size: 50% 80%;
}

La mise en œuvre est également très simple. Sur la base de ce qui précède, jugez si l'unité est %, si oui, canvascalculez la largeur et la hauteur de l'image à afficher en fonction de la largeur et de la hauteur de l'image. La deuxième valeur n'est pas définie ou est, autocomme avant, il est également basé sur le rapport d'aspect de l'image à adapter.

drawBackgroundImageToCanvas(ctx, width, height, this.img, {
    
    
    backgroundSize: '50% 80%'
})

handleBackgroundSize({
    
    
    backgroundSize,
    drawOpt,
    imageRatio,
    canvasWidth: width,// 传参新增canvas的宽高
    canvasHeight: height
})

// 模拟background-size
const handleBackgroundSize = ({
     
      backgroundSize, drawOpt, imageRatio, canvasWidth, canvasHeight }) => {
    
    
  if (backgroundSize) {
    
    
    // ...
    // 图片宽度
    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)
    }
  }
}

L'effet est le suivant :

type de couverture

background-sizeDéfini pour signifier que coverl'image conservera son format d'image d'origine et mis à l'échelle à la taille minimale qui couvrira complètement la zone de positionnement de l'arrière-plan. Notez que l'image ne sera pas déformée.

cssLe style est le suivant :

.cssBox {
    
    
    background-image: url('/3.jpeg');
    background-repeat: no-repeat;
    background-size: cover;
}

Cette mise en œuvre est également très simple. Selon le rapport d'aspect de l'image et le canvasrapport d'aspect de l'image, si la largeur de l'image agrandie est canvasla même que la largeur de l'image, ou la hauteur de l'image canvasest la même que la hauteur de l'image.

drawBackgroundImageToCanvas(ctx, width, height, this.img, {
    
    
    backgroundSize: 'cover'
})

handleBackgroundSize({
    
    
    backgroundSize,
    drawOpt,
    imageRatio,
    canvasWidth: width,
    canvasHeight: height,
    canvasRatio// 参数增加canvas的宽高比
})

const handleBackgroundSize = ({
     
     
  backgroundSize,
  drawOpt,
  imageRatio,
  canvasWidth,
  canvasHeight,
  canvasRatio
}) => {
    
    
    // ...
    // 值为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
    }
    // ...
}

L'effet est le suivant :

contenir le type

background-sizeLe définir sur containtype signifie que l'image conservera toujours le rapport d'aspect d'origine et qu'elle sera mise à l'échelle à la taille maximale adaptée à la zone de positionnement de l'arrière-plan, c'est-à-dire que l'image sera affichée complètement, mais elle ne couvrira pas nécessairement l'arrière-plan horizontalement et verticalement. Il peut y avoir un espace vide dans une direction.

cssstyle:

.cssBox {
    
    
    background-image: url('/1.jpg');
    background-repeat: no-repeat;
    background-size: contain;
}

L'implémentation coverest à l'opposé de l'implémentation du type. Si le rapport d'aspect de l'image est supérieur au rapport d' canvasaspect de l'image, afin de rendre l'image entièrement affichée, la largeur de l'image canvasest cohérente avec la largeur de l'image, et la hauteur est auto-adaptative.

const handleBackgroundSize = () => {
    
    
    // ...
    // 值为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
    }
}

L'effet est le suivant :

background-sizeLa simulation ici est terminée, jetons un coup d'œil background-position.

Simuler la propriété background-position

Regardez d'abord background-sizela situation où il n'est pas défini.

background-positionLa propriété est utilisée pour définir la position de départ de l'image d'arrière-plan, la valeur par défaut est 0% 0%, elle prend également en charge plusieurs types de valeurs différents, voyez-les une par une.

type de pourcentage

La première valeur définit la position horizontale et la deuxième valeur définit la position verticale. Le coin supérieur gauche est 0%0%, le coin inférieur droit est 100%100%, si une seule valeur est définie, la deuxième valeur par défaut est 50%, par exemple, elle est définie sur 50% 60%, ce qui signifie aligner 50% 60%la position de l'image avec la position de la zone d'arrière-plan , et pour exemple , représentant le point central de l'image et le centre des points de la zone d'arrière-plan coïncident.50% 60%50% 50%

cssstyle:

.cssBox {
    
    
    background-image: url('/2.jpg');
    background-repeat: no-repeat;
    background-position: 50% 50%;
}

En termes de mise en œuvre, il suffit d'utiliser les trois paramètres de drawImagela méthode La largeur et la hauteur de l'image ne seront pas mises à l'échelle, et la distance correspondant à l'image est calculée selon le rapport Leur différence est la position de la image affichée sur l'image.imgx、ycanvascanvas

drawBackgroundImageToCanvas(ctx, width, height, this.img, {
    
    
    backgroundPosition: '50% 50%'
})

const drawBackgroundImageToCanvas = () => {
    
    
    // ...
    // 模拟background-position
    handleBackgroundPosition({
    
    
      backgroundPosition,
      drawOpt,
      imgWidth,
      imgHeight,
      canvasWidth: width,
      canvasHeight: height
    })
    // ...
}

// 模拟background-position
const handleBackgroundPosition = ({
     
     
  backgroundPosition,
  drawOpt,
  imgWidth,
  imgHeight,
  canvasWidth,
  canvasHeight
}) => {
    
    
  if (backgroundPosition) {
    
    
    // 将值转换成数组
    let backgroundPositionValueArr = getNumberValueFromStr(backgroundPosition)
    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
      }
      // 垂直位置
      if (backgroundPositionValueArr[1][1] === '%') {
    
    
        // 单位为%
        let canvasY = (backgroundPositionValueArr[1][0] / 100) * canvasHeight
        let imgY = (backgroundPositionValueArr[1][0] / 100) * imgHeight
        // 计算差值
        drawOpt.y = canvasY - imgY
      }
    }
  }
}

L'effet est le suivant :

type de longueur

La première valeur représente la position horizontale et la deuxième valeur représente la position verticale. Le coin supérieur gauche est 0 0. L'unité peut être pxou toute autre cssunité, bien sûr, nous ne considérons que px. Si une seule valeur est spécifiée, les autres seront 50%. Ainsi, vous pouvez mélanger %et assortir px.

cssstyle:

.cssBox {
    
    
    background-image: url('/2.jpg');
    background-repeat: no-repeat;
    background-position: 50px 150px;
}

Cette implémentation est plus simple, il suffit de passer la valeur directement au drawImageparamètre x、y.

drawBackgroundImageToCanvas(ctx, width, height, this.img, {
    
    
    backgroundPosition: '50px 150px'
})

// 模拟background-position
const handleBackgroundPosition = ({
     
     }) => {
    
    
    // ...
    // 水平位置
    if (backgroundPositionValueArr[0][1] === '%') {
    
    
        // ...
    } else {
    
    
        // 其他单位默认都为px
        drawOpt.x = backgroundPositionValueArr[0][0]
    }
    // 垂直位置
    if (backgroundPositionValueArr[1][1] === '%') {
    
    
        // ...
    } else {
    
    
        // 其他单位默认都为px
        drawOpt.y = backgroundPositionValueArr[1][0]
    }
}

type de mot-clé

C'est-à-dire combiner des mots - clés tels que , left, etc. Il peut être considéré comme une valeur spéciale, nous n'avons donc qu'à écrire un mappage pour mapper ces mots-clés à des valeurs en pourcentage.topleft topcenter centercenter bottom%

.cssBox {
    
    
    background-image: url('/2.jpg');
    background-repeat: no-repeat;
    background-position: right bottom;
}
drawBackgroundImageToCanvas(ctx, width, height, this.img, {
    
    
    backgroundPosition: 'right bottom'
})

// 关键词到百分比值的映射
const keyWordToPercentageMap = {
    
    
  left: 0,
  top: 0,
  center: 50,
  bottom: 100,
  right: 100
}

const handleBackgroundPosition = ({
     
     }) => {
    
    
    // ...
    // 将关键词转为百分比
    backgroundPositionValueArr = backgroundPositionValueArr.map(item => {
    
    
      if (typeof item === 'string') {
    
    
        return keyWordToPercentageMap[item] !== undefined
          ? [keyWordToPercentageMap[item], '%']
          : item
      }
      return item
    })
    // ...
}

Combiné avec background-size

Enfin, nous regardons background-sizece qui se passe lorsqu'il est combiné avec et .

.cssBox {
    
    
    background-image: url('/2.jpg');
    background-repeat: no-repeat;
    background-size: cover;
    background-position: right bottom;
}
drawBackgroundImageToCanvas(ctx, width, height, this.img, {
    
    
    backgroundSize: 'cover',
    backgroundPosition: 'right bottom'
})

Le résultat est le suivant :

Incohérent, pourquoi ? Faisons le tri. D'abord, le traitement background-sizecalculera drawImageles paramètres width、height, c'est-à-dire canvasla largeur et la hauteur de l'image qui y est affichée, et background-positionla largeur et la hauteur de l'image seront utilisées dans le traitement, mais ce que nous passons est toujours l'image La largeur et la hauteur d'origine de , bien sûr il y a un problème avec ce calcul, modifiez-le :

// 模拟background-position
handleBackgroundPosition({
    
    
    backgroundPosition,
    drawOpt,
    imgWidth: drawOpt.width,// 改为传计算后的图片的显示宽高
    imgHeight: drawOpt.height,
    imageRatio,
    canvasWidth: width,
    canvasHeight: height,
    canvasRatio
})

Maintenant, regardez à nouveau l'effet :

Simuler la propriété background-repeat

background-repeatL'attribut est utilisé pour définir comment mosaïquer l'objet background-image. La valeur par défaut repeatest, c'est-à-dire que lorsque l'image est plus petite que la zone d'arrière-plan, elle se répétera verticalement et horizontalement par défaut. Il existe plusieurs valeurs facultatives :

  • repeat-x: seule la position horizontale répétera l'image d'arrière-plan
  • repeat-y: Seule la position verticale répétera l'image d'arrière-plan
  • no-repeat: background-imagene se répétera pas

Ensuite, nous implémentons ces situations.

sans répétition

Tout d'abord, jugez si la largeur et la hauteur de l'image sont plus grandes que la zone d'arrière-plan. Si c'est le cas, il n'est pas nécessaire de mosaïquer ou de traiter, et l'autre valeur n'a pas besoin d'être traitéeno-repeat :

// 模拟background-repeat
handleBackgroundRepeat({
    
    
    backgroundRepeat,
    drawOpt,
    imgWidth: drawOpt.width,
    imgHeight: drawOpt.height,
    imageRatio,
    canvasWidth: width,
    canvasHeight: height,
    canvasRatio
})

Vous pouvez voir que la largeur et la hauteur de l'image que nous téléchargeons ici sont également la background-sizelargeur et la hauteur d'affichage de l'image calculées.

// 模拟background-repeat
const handleBackgroundRepeat = ({
     
     
  backgroundRepeat,
  drawOpt,
  imgWidth,
  imgHeight,
  canvasWidth,
  canvasHeight,
}) => {
    
    
    if (backgroundRepeat) {
    
    
        // 将值转换成数组
        let backgroundRepeatValueArr = getNumberValueFromStr(backgroundRepeat)
        // 不处理
        if (backgroundRepeatValueArr[0] === 'no-repeat' || (imgWidth >= canvasWidth && imgHeight >= canvasHeight)) {
    
    
            return
        }
    }
}

répéter-x

Ensuite, ajoutez repeat-xla prise en charge de la paire. Lorsque canvasla largeur est supérieure à la largeur de l'image, la mosaïque horizontale sera dessinée et le dessin appellera la drawImageméthode à plusieurs reprises, il est donc nécessaire de transmettre ctxdes imageparamètres à handleBackgroundRepeatla méthode. De plus, si handleBackgroundRepeatle dessin est effectué dans la méthode, la méthode de dessin d'origine n'est pas nécessaire d'appeler :

// 模拟background-repeat
// 如果在handleBackgroundRepeat里进行了绘制,那么会返回true
let notNeedDraw = handleBackgroundRepeat({
    
    
    ctx,
    image,
    ...
})
if (!notNeedDraw) {
    
    
    drawImage(ctx, image, drawOpt)
}

// 根据参数绘制图片
const drawImage = (ctx, image, drawOpt) => {
    
    
  ctx.drawImage(
    image,
    drawOpt.sx,
    drawOpt.sy,
    drawOpt.swidth,
    drawOpt.sheight,
    drawOpt.x,
    drawOpt.y,
    drawOpt.width,
    drawOpt.height
  )
}

La méthode de dessin est extraite dans une méthode pour une réutilisation facile.

const handleBackgroundRepeat = ({
     
     }) => {
    
    
    // ...
    // 水平平铺
    if (backgroundRepeatValueArr[0] === 'repeat-x') {
    
    
      if (canvasWidth > imgWidth) {
    
    
        let x = 0
        while (x < canvasWidth) {
    
    
          drawImage(ctx, image, {
    
    
            ...drawOpt,
            x
          })
          x += imgWidth
        }
        return true
      }
    }
    // ...
}

Chaque fois que le xparamètre de position de placement de l'image est mis à jour jusqu'à ce que canvasla largeur dépasse.

répéter

La bonne repeat-ymanipulation est similaire :

const handleBackgroundRepeat = ({
     
     }) => {
    
    
    // ...
    // 垂直平铺
    if (backgroundRepeatValueArr[0] === 'repeat-y') {
    
    
      if (canvasHeight > imgHeight) {
    
    
        let y = 0
        while (y < canvasHeight) {
    
    
          drawImage(ctx, image, {
    
    
            ...drawOpt,
            y
          })
          y += imgHeight
        }
        return true
      }
    }
    // ...
}

répéter

Et enfin repeatla valeur, qui se répète à la fois horizontalement et verticalement :

const handleBackgroundRepeat = ({
     
     }) => {
    
    
    // ...
    // 平铺
    if (backgroundRepeatValueArr[0] === 'repeat') {
    
    
      let x = 0
      while (x < canvasWidth) {
    
    
        if (canvasHeight > imgHeight) {
    
    
          let y = 0
          while (y < canvasHeight) {
    
    
            drawImage(ctx, image, {
    
    
              ...drawOpt,
              x,
              y
            })
            y += imgHeight
          }
        }
        x += imgWidth
      }
      return true
    }
}

De gauche à droite, il est tracé colonne par colonne, horizontalement jusqu'à la largeur xexcédentaire canvaset verticalement jusqu'à la hauteur yexcédentaire .canvas

Combinaison avec background-size, background-position

Enfin, regardez la combinaison avec les deux premiers attributs.

cssstyle:

.cssBox {
    
    
    background-image: url('/4.png');
    background-repeat: repeat;
    background-size: 50%;
    background-position: 50% 50%;
}

L'effet est le suivant :

La taille de l'image est correcte, mais la position est incorrecte. La cssmeilleure façon de le faire est de commencer par background-positionpositionner une image en fonction de la valeur de , puis de la mettre en mosaïque, mais nous ignorons évidemment cette situation et 0 0commençons à dessiner à partir de la position à chaque fois.

Connaissant le principe, la solution est aussi très simple, elle handleBackgroundPositiona été calculée dans la méthode x、y, c'est-à-dire la position de placement de la première image avant le pavage :

Nous avons seulement besoin de calculer combien d'images peuvent être carrelées à gauche et en haut, et de calculer la position de la première image dans les directions horizontale et verticale comme valeur x、yinitiale du cycle suivant.

const handleBackgroundRepeat = ({
     
     }) => {
    
    
    // 保存在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
    // 将oxRepeatX和oxRepeatY作为后续循环的x、y的初始值
    // ...
    // 平铺
    if (backgroundRepeatValueArr[0] === 'repeat') {
    
    
      let x = oxRepeatX
      while (x < canvasWidth) {
    
    
        if (canvasHeight > imgHeight) {
    
    
          let y = oxRepeatY
          // ...
        }
      }
    }
}

fin

Cet article réalise simplement certains des effets des trois attributs canvassimulés dans , et . Le code source complet cssse trouve sur https://github.com/wanglin2/simulateCSSBackgroundInCanvas .background-sizebackground-positionbackground-repeat

Je suppose que tu aimes

Origine blog.csdn.net/sinat_33488770/article/details/129253709
conseillé
Classement