Skip to content

Instantly share code, notes, and snippets.

@natew
Last active July 6, 2019 05:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save natew/55d8e1ae1a739c75f0d7ebecc22c8698 to your computer and use it in GitHub Desktop.
Save natew/55d8e1ae1a739c75f0d7ebecc22c8698 to your computer and use it in GitHub Desktop.
import { animation, Button, Card, Geometry, Row, Title, View } from '@o/ui'
import React from 'react'
export const TestCarousel = () => {
const apps = [{ title: 'ok' }, { title: 'ok2' }, { title: 'ok3' }, { title: 'o4' }]
const carousel = animation(() => ({
scrollLeft: 0,
zoomed: false,
}))
/**
* animation() is a helper, but desugars into props somewhat like:
* scrollLeft={spring(...)}
* animated
* lets us control it using .set without being fully controlled
*/
return (
<View width="100%" height="100%">
<Button onClick={() => carousel.set(cur => ({ zoomed: !cur.zoomed }))}>zoom</Button>
<Button onClick={() => carousel.set(cur => ({ scrollLeft: 100 + cur.scrollLeft }))}>
scroll 100px right
</Button>
<Row flex={1} scrollable="x" space={100} {...carousel}>
{apps.map((app, index) => (
<Geometry key={index}>
{geometry => (
<Card
onClick={() => {
carousel.set({
scrollLeft: geometry.offset(index),
})
}}
onDoubleClick={() => {
carousel.set(current => ({
scrollLeft: geometry.offset(index),
zoomed: !current.zoomed,
}))
}}
transform={{
rotateY: () => (index - geometry.frame(index)) * 10,
scale: () => (carousel.zoomed ? 1 : Math.abs(geometry.frame(index))),
}}
>
<Title>{app.title}</Title>
</Card>
)}
</Geometry>
))}
</Row>
</View>
)
}
@natew
Copy link
Author

natew commented Jul 5, 2019

The alternative I see is basically having everything work through the ref of the Row, but that opens up a few problems.

In this example we have this custom zoomed property, which I extracted based on an example. I should really post the example to make this clear, which will be my next step. But basically, you can focus out of the cards / focus into any single card using zoom. I'll need to upgrade this demo quite a bit before that's clear.

But the custom property feels weird in this alternate attempt:

export const TestCarousel2 = () => {
  const apps = [{ title: 'ok' }, { title: 'ok2' }, { title: 'ok3' }, { title: 'o4' }]
  const carousel = useRef<Row>(null)
  return (
    <View width="100%" height="100%">
      <Button onClick={() => carousel.current.setProp('zoomed', !carousel.current.props.zoomed)}>
        zoom
      </Button>
      <Button onClick={() => carousel.current.setProp('scrollLeft', cur => cur + 100)}>
        scroll 100px right
      </Button>
      {/* this parts a bit sketchy */}
      <Row
        ref={carousel}
        flex={1}
        scrollable="x"
        space={100}
        animated={{ scrollLeft: true, zoomed: true }}
        defaultScrollLeft={0}
        defaultZoomed={false}
      >
        {apps.map((app, index) => (
          <Geometry key={index} frame={carousel}>
            {geometry => (
              <Card
                onClick={() => {
                  carousel.current.setProp('scrollLeft', geometry.offset(index))
                }}
                onDoubleClick={() => {
                  carousel.current.setProps(cur => ({
                    scrollLeft: geometry.offset(index),
                    zoomed: !cur.zoomed,
                  }))
                }}
                transform={{
                  rotateY: () => (index - geometry.frame(index)) * 10,
                  scale: () =>
                    carousel.current.props.zoomed ? 1 : Math.abs(geometry.frame(index)),
                }}
              >
                <Title>{app.title}</Title>
              </Card>
            )}
          </Geometry>
        ))}
      </Row>
    </View>
  )
}

Since Row/Col and other views all extend the base View, which handles the animation stuff, this would work. The changes from current would be:

  1. Right now View refs return a handle for the DOM node, so you'd have to move that into ref.node
  2. You'd have to have completely custom property support.
  3. You'd need to scan props for default[Property] as well.

Keep in mind this is all pretty sketchy code golfing, likely a number of typos and poorly thought through peices.

@natew
Copy link
Author

natew commented Jul 6, 2019

This one is simpler and a bit clearer:

export const TestCarousel = () => {
  const apps = ['ok', 'ok2', 'ok3', 'ok4']
  const [zoom, setZoom] = useState(false)
  const carousel = useRef<HTMLElement>(null)
  return (
    <View width="100%" height="100%">
      <Button onClick={() => setZoom(!zoom)}>zoom</Button>
      <Button onClick={() => (carousel.current.scrollLeft += 100)}>scroll 100px right</Button>
      <Row ref={carousel} flex={1} scrollable="x" space={100}>
        {apps.map((app, index) => (
          <Geometry key={index} frame={carousel}>
            {geometry => (
              <Card
                onClick={() => {
                  carousel.current.scrollLeft = geometry.offset(index)
                }}
                onDoubleClick={() => {
                  carousel.current.scrollLeft = geometry.offset(index)
                  setZoom(false)
                }}
                animation={{
                  mass: 10,
                  friction: 20,
                  velocity: 100,
                }}
                transform={{
                  rotateY: () => (index - geometry.frame(index)) * 10,
                  scale: () => (zoom ? 1 : Math.abs(geometry.frame(index))),
                }}
              >
                <Title>{app}</Title>
              </Card>
            )}
          </Geometry>
        ))}
      </Row>
    </View>
  )
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment