キャンバスでcssの背景画像スタイルをシミュレートする方法

著者は Web マインドマップmind-mapをオープンソース化しています. 最近, 背景画像の効果を最適化する際に問題が発生しました. ページに表示するときは をcss使用して背景画像をbackground-imageレンダリングしますが、エクスポートすると背景画像が背景画像の背景画像は、サイズ、位置、繰り返しの設定など、より豊富な効果をサポートしてcanvasいますが、著者は1つの方法しか見つけられず、繰り返しの設定のみをサポートしています効果なので、あるものをシミュレートする方法 背景効果については、これで終わりではなく、次に一緒に試してみましょう。cssbackground-sizebackground-positionbackground-repeatcanvascreatePattern()canvascss

最初に説明することは、すべての効果を完全かつ完全に100%シミュレートするわけではないということです.強力すぎるため、属性値の組み合わせは非常に柔軟であり、多くの種類があり、その中にはユニットの種類が多く、したがって、いくつかの一般的な状況のみがシミュレーされ、ユニットのみが考慮されますcsscsspx%

canvasちなみに、この記事を読んだ後、バックグラウンドで設定されたいくつかの属性の方法drawImageと使用方法も確認できますcss

canvas の drawImage() メソッド

一般に、canvasこのdrawImage()メソッドを使用して背景画像を描画します. 最初にこのメソッドを見てみましょう. このメソッドはさらにパラメータを受け取ります:

必要なパラメーターは 3 つだけです。

基本的なフレームワークとツール

コア ロジックは、画像をロードし、メソッドを使用して画像を描画することです。これは、さまざまな属性と値に従って計算されたパラメーターdrawImageにすぎないため、関数の次の基本的なフレームワークを記述できます。cssdrawImage

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)
  }
}

次に、いくつかのツール機能を見てみましょう。

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

cssの属性値は文字列型や数値型などで、100px 100% autoそのまま使うと不便なので[[100, 'px'], [100, '%'], 'auto']フォームに変換します。

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

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

元の比率と新しい幅または高さに基づいて、スケーリングされた幅または高さを計算します。

background-size プロパティをシミュレートする

デフォルトbackground-repeat値は ですrepeat。重複の場合は考慮しないため、 に設定しますno-repeat

background-sizeこの属性は、背景画像のサイズを設定するために使用され、順番にシミュレートされる 4 種類の値を受け入れることができます。

長さタイプ

背景画像の高さと幅を設定します。最初の値は幅を設定し、2 番目の値は高さを設定します。値が 1 つだけ指定されている場合、2 番目の値はデフォルトでauto (自動) になります。

cssスタイルは次のとおりです。

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

値が 1 つだけ設定されている場合は、背景画像表示の実際の幅を表します。高さが設定されていない場合は、画像の縦横比に応じて自動的にスケーリングされます。効果は次のとおりです。

のシミュレーションは非常に単純で、4 つのパラメーターをメソッドcanvasに渡す必要があります: 画像を表す、キャンバスに画像を配置する位置を表す、特別な設定はありませサイズ,値が1つしか渡されない場合,この値に直接設定し,画像の縦横比に応じて計算します. 2つの値が渡された場合, 2つの値を別々に渡します. さらに, あなたは必要です次のように値を処理しますdrawImageimg、x、y、width、heightimgx、y0、0width、heightbackground-sizewidthheightwidth、heightauto

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)
      }
    }
}

効果は次のとおりです。

2 つの値を設定する効果:

background-size: 300px 400px;

パーセンテージ タイプ

背景に対するローカライズされた領域の割合が計算されます。最初の値は幅のパーセンテージを設定し、2 番目の値は高さのパーセンテージを設定します。値が 1 つだけ指定されている場合、2 番目の値はデフォルトでauto (自動) になります。たとえば、 set の場合、画像は背景領域の幅と高さ50% 80%に合わせてスケーリングされます。50%80%

cssスタイルは次のとおりです。

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

実装も非常に簡単. 上記に基づいて, 単位が であるかどうかを判断し, ある%場合,canvas画像の幅と高さに応じて表示される画像の幅と高さを計算します. 2番目の値は設定されていません.または、auto以前と同様に、適応する画像の縦横比にも基づいています。

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)
    }
  }
}

効果は次のとおりです。

カバータイプ

background-sizecover画像が元の縦横比を維持し、背景の配置領域を完全にカバーする最小サイズにスケーリングされることを意味するように設定します。画像は変形されません。

cssスタイルは次のとおりです。

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

この実装も非常に単純です. 画像の縦横比とcanvas画像の縦横比に応じて, ズームされた画像の幅がcanvas画像の幅と同じになるか, 画像の高さが画像canvasと同じになります.写真の高さ。

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
    }
    // ...
}

効果は次のとおりです。

タイプを含む

background-sizeタイプに設定するとcontain、画像は元の縦横比を維持し、背景の配置領域に適した最大サイズにスケーリングされます。つまり、画像は完全に表示されますが、必ずしも背景を覆うとは限りません。横と縦の一方向に余白があっても構いません。

cssスタイル:

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

実装はcover型の実装とちょうど反対です. ピクチャのアスペクト比がピクチャcanvasのアスペクト比よりも大きい場合, ピクチャを完全に表示するために, ピクチャの幅はの幅canvasと一致しています.高さは自己適応型です。

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
    }
}

効果は次のとおりです。

ここまでのbackground-sizeシミュレーションは終わったので、見てみましょうbackground-position

background-position プロパティをシミュレートする

まず、background-size設定されていない状況を見てください。

background-positionこのプロパティは、背景画像の開始位置を設定するために使用されます。デフォルト値は です 0% 0%。また、いくつかの異なるタイプの値をサポートしています。それらを 1 つずつ参照してください。

パーセンテージ タイプ

最初の値は水平位置を設定し、2 番目の値は垂直位置を設定します。左上隅は0%0%、右下隅は です100%100%。値が 1 つだけ設定されている場合、2 番目のデフォルトは ですたとえば50%、 に設定されています。例: 画像の中心点と背景領域の中心点が一致していることを表します。50% 60%50% 60%50% 60%50% 50%

cssスタイル:

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

実装に関しては,drawImageメソッドimgの3 つのパラメータのみを使用x、yする必要があります. 画像の幅と高さはスケーリングされず, 画像に対応する距離は比率に従って計算されますcanvas. それらの差はcanvas画像の位置です.画像に表示される画像。

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
      }
    }
  }
}

効果は次のとおりです。

長さタイプ

最初の値は水平位置を表し、2 番目の値は垂直位置を表します。左上隅は0 0. 単位はpxまたはその他のcss単位にすることができます。もちろん、 のみを考慮しますpx値が 1 つだけ指定されている場合、他の値は になります50%だから、ミックス%してマッチさせることができますpx

cssスタイル:

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

この実装はより単純で、値をパラメーターに直接渡すだけdrawImageです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]
    }
}

キーワード タイプ

leftなどtopキーワードを組み合わせることですこれは特別な値と見なすことができるため、これらのキーワードをパーセンテージ値にマップするためのマッピングを記述するだけで済みます。left 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
    })
    // ...
}

background-size と組み合わせる

background-size最後に、 と を組み合わせるとどうなるかを見ていきます。

.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'
})

結果は次のとおりです。

矛盾している、なぜこれなのか? 整理してみましょう. まず、処理はパラメーター、つまり、その中に表示される画像の幅と高さをbackground-size計算し、画像の幅と高さを処理に使用しますが、私たちが渡すものはまだ絵です の元の幅と高さ、もちろんこの計算には問題があります、それを修正してください:drawImagewidth、heightcanvasbackground-position

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

効果をもう一度見てみましょう。

background-repeat プロパティをシミュレートする

background-repeatこの属性は、オブジェクトをどのように並べるかを設定するために使用されますbackground-image。デフォルト値はrepeat、つまり、イメージが背景領域より小さい場合、デフォルトで垂直方向および水平方向に繰り返されます。いくつかのオプション値があります:

  • repeat-x: 水平位置のみが背景画像を繰り返します
  • repeat-y: 垂直位置のみ背景画像を繰り返します
  • no-repeat:background-image繰り返さない

次に、これらの状況を実装します。

繰り返しなし

まず、画像の幅と高さが背景領域よりも大きいかどうかを判断します. 大きい場合は、タイルまたは処理する必要はなく、他の値は処理する必要はありませんno-repeat:

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

ここでアップロードする画像の幅と高さも、計算されたbackground-size画像表示の幅と高さであることがわかります。

// 模拟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
        }
    }
}

繰り返し-x

次に、repeat-xペアのサポートを追加します.canvas幅が画像の幅よりも大きい場合、横方向のタイルが描画され、描画はメソッドを繰り返し呼び出すため、メソッドにパラメーターdrawImageを渡す必要があります.また、メソッド内で描画を行う場合は、元の描画メソッドを呼び出す必要はありません:ctximagehandleBackgroundRepeathandleBackgroundRepeat

// 模拟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
  )
}

描画メソッドは、再利用しやすいメソッドに抽出されます。

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
      }
    }
    // ...
}

幅を超えるまで、画像の配置位置xパラメーターが更新されるたびに。canvas

繰り返し-y

正しいrepeat-y処理は似ています:

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
      }
    }
    // ...
}

繰り返す

そして最後にrepeat、水平方向と垂直方向の両方で繰り返される値:

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
    }
}

左から右に、列ごとに、x余分なcanvas幅まで水平方向に、y余分なcanvas高さまで垂直方向に描画されます。

background-size、background-position との組み合わせ

最後に、最初の 2 つの属性との組み合わせを見てください。

cssスタイル:

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

効果は次のとおりです。

画像のサイズは正しいが、位置が正しくない.cssこれを行う最善の方法は、最初にbackground-positionの値に従って画像を配置し、次に並べて並べることですが、明らかにこの状況を無視して、0 0その位置から描画を開始します毎回。

原理を知っていれば、解決策も非常に簡単です.handleBackgroundPosition方法で計算されていますx、y.つまり、タイリング前の最初の画像の配置位置:

x、y左と上に何枚の画像を並べることができるかを計算し、次のサイクルの初期値として水平方向と垂直方向の最初の画像の位置を計算するだけです。

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
          // ...
        }
      }
    }
}

終わり

この記事では、 、およびcanvasでシミュレートされた3 つの属性の効果の一部を単純に理解しています。完全なソース コードはhttps://github.com/wanglin2/simulateCSSBackgroundInCanvascssにありますbackground-sizebackground-positionbackground-repeat

おすすめ

転載: blog.csdn.net/sinat_33488770/article/details/129253709