Home > database >  How can I position a span that depends on it's getBoundingClientRect with some calculations
How can I position a span that depends on it's getBoundingClientRect with some calculations

Time:03-19

I want something like this:

const App = () => {

  let [bounds, setBounds] = createSignal()
  let $piano

  onMount(() => {
    setBounds($piano.getBoundingClientRect())

    let key_xys = createMemo(() => {
       let _bounds = bounds()

       if (!_bounds) { return [] }
       // some calculation about bounds
       return [_bounds.left, _bounds.right]
    })
  })

  return (<PianoView ref={$piano} key_xys={key_xys}/>)
}


const PianoView = (props) => {

  let { ref, key_xys } = props

  return (<div class='p-wrap' ref={ref}>
    <span style={transform: `translate(0, ${key_xys[0]})`}/>
  </div>)
}

So I want to position a span that depends on it's bounds on the dom with some calculations.

Of course the above example doesn't work because key_xys the calculated value doesn't exist at creation time.

CodePudding user response:

Two issues.

  1. key_xys value is an Accessor, basically a function to retrieve the value (which createMemo and createSignal returns), so in PianoView jsx, you must call it.
<span style={`transform: translate(0, ${key_xys()[0]}px)`}/>

Also in SolidJS, the style attribute can be a string like native html, which I decided to write as.

  1. As you noted, this application won't run because key_xys is not defined, because you declared it inside onMount callback. There's no need to do this. Move the key_xys declaration in App component scope.
const App = () => {
  let [bounds, setBounds] = createSignal();
  let $piano;
  let key_xys = createMemo(() => {
    let _bounds = bounds() as any;

    if (!_bounds) {
      return [];
    }
    // some calculation about bounds
    return [_bounds.left, _bounds.right];
  });

  onMount(() => {
    setBounds($piano.getBoundingClientRect());
  });

  return <PianoView ref={$piano} key_xys={key_xys} />;
};

Code Answer

Full code answer below.

const App = () => {
  let [bounds, setBounds] = createSignal();
  let $piano;
  let key_xys = createMemo(() => {
    let _bounds = bounds() as any;

    if (!_bounds) {
      return [];
    }
    // some calculation about bounds
    return [_bounds.left, _bounds.right];
  });

  onMount(() => {
    setBounds($piano.getBoundingClientRect());
  });

  return <PianoView ref={$piano} key_xys={key_xys} />;
};

const PianoView = (props) => {
  const {ref, key_xys} = props
  return (
    <div  ref={ref}>
    hiii
      <span style={`transform: translate(0, ${key_xys()[0]}px)`} />
    </div>
  );
};

CodePudding user response:

I believe you can do this without a signal. Just make key_xys derived state based on the ref. Also, make sure to call the accessor key_xys() when passing it to the Piano component. Finally, make sure not to destructure props since any reactive props will lose their reactivity.

const App = () => {
  let $piano;

  const key_xys = () => {
    const bounds = $piano?.getBoundingClientRect();
    if (!bounds) return [0, 0];
    return [bounds.left, bounds.right];
  };

  return <PianoView ref={$piano} key_xys={key_xys()} />;
};

const PianoView = (props) => {
  return (
    <div  ref={props.ref}>
      <span style={{ transform: `translate(0, ${props.key_xys[0]})` }} />
    </div>
  );
};
  • Related