🏷️

Cytoscape.js: The style value of `label` is deprecated for `width`

2023/06/26に公開

Cytoscape.jsでノードの横幅をラベル文字の横幅に合わせたい場合に使えたwidth: 'label'オプションが、v3.16から非推薦となっている[1]

cytoscape({
  style: [
    {
      selector: 'node',
      style: {
        shape: 'rectangle',
        width: 'label' // The style value of `label` is deprecated for `width`
      }
    }
  ]
})

さらに、このオプションでは図のようにラベルの前後にパディングがなく、見た目が悪い。

width: 'label'

マッパーの作成

ドキュメントには「プロパティの値を直接指定する代わりにマッパーを用いて動的にプロパティ値を指定することもできる[2]」とあるので、ラベル文字からキャンバスに描画された文字の横幅を取得するマッパーを作成する。

labelWidth.ts
import type { NodeCollection } from 'cytoscape'

const labelWidth = (node: NodeCollection): number => {
  const canvas = document.createElement('canvas')
  const context = canvas.getContext('2d')!
  context.font = ['style', 'weight', 'size', 'family']
    .map(style => node.style(`font-${style}`))
    .join(' ')
  const { width } = context.measureText(node.data('label'))
  const padding = 10

  return width + padding
}

export { labelWidth }
JavaScriptの場合
labelWidth.js
const labelWidth = node => {
  const canvas = document.createElement('canvas')
  const context = canvas.getContext('2d')
  context.font = ['style', 'weight', 'size', 'family']
    .map(style => node.style(`font-${style}`))
    .join(' ')
  const { width } = context.measureText(node.data('label'))
  const padding = 10

  return width + padding
}

マッパーの使用

widthオプションに作成したマッパーを指定する。

+ import { labelWidth } from 'labelWidth.ts'

  cytoscape({
    style: [
      {
        selector: 'node',
        style: {
          shape: 'rectangle',
-         width: 'label' // The style value of `label` is deprecated for `width`
+         width: labelWidth
        }
      }
    ]
  })

いい感じになった。

width: labelWidth

余談

ワーニングの文字列「The style value of label is deprecated for width」でググると、おそらくcytoscape/cytoscape.js#2713を参考にしたてあろう下記のようなマッパーがいくつかヒットしたが、自分で試したところ上手くいかなかった。

const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
const fontStyle = node.style('font-style')
const fontSize = node.style('font-size')
const fontFamily = node.style('font-family')
const fontWeight = node.style('font-weight')
context.font = fontStyle + ' ' + fontWeight + ' ' + fontSize + ' ' + fontFamily

調査したところ、原因は node.style()を適用する順番 にあることがわかった。
MDN Web docsによると、CSSのfontプロパティの値は次の順番で指定しなければならない[3]

font: font-style font-weight font-size font-family

上記コードにおいてcontext.fontの指定順は問題ないが、その前のnode.style()の適用においてもこの順番を守る必要があったようである。
つまり、次のように適用順番を修正すれば、上記コードでも正しく機能する。

  const canvas = document.createElement('canvas')
  const context = canvas.getContext('2d')
  const fontStyle = node.style('font-style')
+ const fontWeight = node.style('font-weight')
  const fontSize = node.style('font-size')
  const fontFamily = node.style('font-family')
- const fontWeight = node.style('font-weight')
  context.font = fontStyle + ' ' + fontWeight + ' ' + fontSize + ' ' + fontFamily
脚注
  1. Deprecate width:label & height:label ↩︎

  2. Mappers ↩︎

  3. font - CSS: カスケーディングスタイルシート | MDN ↩︎

Discussion