Add a getKeyframes method to Sequence (#426)
This commit is contained in:
parent
35fe1c375c
commit
4db16f1a7e
3 changed files with 123 additions and 0 deletions
62
theatre/core/src/sequences/Sequence.test.ts
Normal file
62
theatre/core/src/sequences/Sequence.test.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
import {setupTestSheet} from '@theatre/shared/testUtils'
|
||||
import {encodePathToProp} from '@theatre/shared/utils/addresses'
|
||||
import {asKeyframeId, asSequenceTrackId} from '@theatre/shared/utils/ids'
|
||||
import type {ObjectAddressKey, SequenceTrackId} from '@theatre/shared/utils/ids'
|
||||
|
||||
describe(`Sequence`, () => {
|
||||
test('sequence.getKeyframesOfSimpleProp()', async () => {
|
||||
const {objPublicAPI, sheet} = await setupTestSheet({
|
||||
staticOverrides: {
|
||||
byObject: {},
|
||||
},
|
||||
sequence: {
|
||||
type: 'PositionalSequence',
|
||||
length: 20,
|
||||
subUnitsPerUnit: 30,
|
||||
tracksByObject: {
|
||||
['obj' as ObjectAddressKey]: {
|
||||
trackIdByPropPath: {
|
||||
[encodePathToProp(['position', 'y'])]: asSequenceTrackId('1'),
|
||||
},
|
||||
trackData: {
|
||||
['1' as SequenceTrackId]: {
|
||||
type: 'BasicKeyframedTrack',
|
||||
keyframes: [
|
||||
{
|
||||
id: asKeyframeId('0'),
|
||||
position: 10,
|
||||
connectedRight: true,
|
||||
handles: [0.5, 0.5, 0.5, 0.5],
|
||||
type: 'bezier',
|
||||
value: 3,
|
||||
},
|
||||
{
|
||||
id: asKeyframeId('1'),
|
||||
position: 20,
|
||||
connectedRight: false,
|
||||
handles: [0.5, 0.5, 0.5, 0.5],
|
||||
type: 'bezier',
|
||||
value: 6,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const seq = sheet.publicApi.sequence
|
||||
|
||||
const keyframes = seq.__experimental_getKeyframes(
|
||||
objPublicAPI.props.position.y,
|
||||
)
|
||||
expect(keyframes).toHaveLength(2)
|
||||
expect(keyframes[0].value).toEqual(3)
|
||||
expect(keyframes[1].value).toEqual(6)
|
||||
expect(keyframes[0].position).toEqual(10)
|
||||
})
|
||||
})
|
|
@ -1,5 +1,6 @@
|
|||
import type Project from '@theatre/core/projects/Project'
|
||||
import type Sheet from '@theatre/core/sheets/Sheet'
|
||||
import {encodePathToProp} from '@theatre/shared/utils/addresses'
|
||||
import type {SequenceAddress} from '@theatre/shared/utils/addresses'
|
||||
import didYouMean from '@theatre/shared/utils/didYouMean'
|
||||
import {InvalidArgumentError} from '@theatre/shared/utils/errors'
|
||||
|
@ -20,10 +21,12 @@ import type {
|
|||
} from './playbackControllers/DefaultPlaybackController'
|
||||
import DefaultPlaybackController from './playbackControllers/DefaultPlaybackController'
|
||||
import TheatreSequence from './TheatreSequence'
|
||||
import type {Keyframe} from '@theatre/core/projects/store/types/SheetState_Historic'
|
||||
import type {ILogger} from '@theatre/shared/logger'
|
||||
import type {ISequence} from '..'
|
||||
import {notify} from '@theatre/shared/notify'
|
||||
import type {$IntentionalAny} from '@theatre/dataverse/src/types'
|
||||
import {isSheetObject} from '@theatre/shared/instanceTypes'
|
||||
|
||||
export type IPlaybackRange = [from: number, to: number]
|
||||
|
||||
|
@ -114,6 +117,46 @@ export default class Sequence implements PointerToPrismProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a pointer to a property of a SheetObject and returns the keyframes of that property.
|
||||
*
|
||||
* Theoretically, this method can be called from inside a prism so it can be reactive.
|
||||
*/
|
||||
getKeyframesOfSimpleProp<V>(prop: Pointer<any>): Keyframe[] {
|
||||
const {path, root} = getPointerParts(prop)
|
||||
|
||||
if (!isSheetObject(root)) {
|
||||
throw new InvalidArgumentError(
|
||||
'Argument prop must be a pointer to a SheetObject property',
|
||||
)
|
||||
}
|
||||
|
||||
const trackP = val(
|
||||
this._project.pointers.historic.sheetsById[this._sheet.address.sheetId]
|
||||
.sequence.tracksByObject[root.address.objectKey],
|
||||
)
|
||||
|
||||
if (!trackP) {
|
||||
return []
|
||||
}
|
||||
|
||||
const {trackData, trackIdByPropPath} = trackP
|
||||
const objectAddress = encodePathToProp(path)
|
||||
const id = trackIdByPropPath[objectAddress]
|
||||
|
||||
if (!id) {
|
||||
return []
|
||||
}
|
||||
|
||||
const track = trackData[id]
|
||||
|
||||
if (!track) {
|
||||
return []
|
||||
}
|
||||
|
||||
return track.keyframes
|
||||
}
|
||||
|
||||
get positionFormatter(): ISequencePositionFormatter {
|
||||
return this._positionFormatterD.getValue()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import {privateAPI, setPrivateAPI} from '@theatre/core/privateAPIs'
|
|||
import {defer} from '@theatre/shared/utils/defer'
|
||||
import type Sequence from './Sequence'
|
||||
import type {IPlaybackDirection, IPlaybackRange} from './Sequence'
|
||||
import type {Keyframe} from '@theatre/core/projects/store/types/SheetState_Historic'
|
||||
import AudioPlaybackController from './playbackControllers/AudioPlaybackController'
|
||||
import {getCoreTicker} from '@theatre/core/coreTicker'
|
||||
import type {Pointer} from '@theatre/dataverse'
|
||||
|
@ -133,6 +134,19 @@ export interface ISequence {
|
|||
position: number
|
||||
}>
|
||||
|
||||
/**
|
||||
* Given a property, returns a list of keyframes that affect that property.
|
||||
*
|
||||
* @example
|
||||
* Usage:
|
||||
* ```ts
|
||||
* // let's assume `sheet` is a sheet and obj is one of its objects
|
||||
* const keyframes = sheet.sequence.__experimental_getKeyframes(obj.pointer.x)
|
||||
* console.log(keyframes) // an array of keyframes
|
||||
* ```
|
||||
*/
|
||||
__experimental_getKeyframes(prop: Pointer<{}>): Keyframe[]
|
||||
|
||||
/**
|
||||
* Attaches an audio source to the sequence. Playing the sequence automatically
|
||||
* plays the audio source and their times are kept in sync.
|
||||
|
@ -291,6 +305,10 @@ export default class TheatreSequence implements ISequence {
|
|||
privateAPI(this).position = position
|
||||
}
|
||||
|
||||
__experimental_getKeyframes(prop: Pointer<any>): Keyframe[] {
|
||||
return privateAPI(this).getKeyframesOfSimpleProp(prop)
|
||||
}
|
||||
|
||||
async attachAudio(args: IAttachAudioArgs): Promise<{
|
||||
decodedBuffer: AudioBuffer
|
||||
audioContext: AudioContext
|
||||
|
|
Loading…
Reference in a new issue