diff options
| author | lonkaars <loek@pipeframe.xyz> | 2021-06-27 10:04:46 +0200 | 
|---|---|---|
| committer | lonkaars <loek@pipeframe.xyz> | 2021-06-27 10:04:46 +0200 | 
| commit | ffefeed888fe6f221a79575f8624257571eacf8d (patch) | |
| tree | a40721186f380e51d071ec9385e04a6ef78c9558 | |
| parent | 94824d7b8a12eb0d9d55935649c477ba65abe780 (diff) | |
loop selection working + fix #1
| -rw-r--r-- | components/selection.tsx | 6 | ||||
| -rw-r--r-- | pages/editor.tsx | 66 | ||||
| -rw-r--r-- | styles/selection.css | 8 | ||||
| -rw-r--r-- | timeline.ts | 5 | 
4 files changed, 50 insertions, 35 deletions
| diff --git a/components/selection.tsx b/components/selection.tsx index 4be9767..618d854 100644 --- a/components/selection.tsx +++ b/components/selection.tsx @@ -90,14 +90,16 @@ export default function Selection(props: {  	right?: slideTypes;  	className?: string;  	widthOffset?: number; +	visibility?: number;  }) { -	var small = (props.width + props.widthOffset) < 24 || props.height < 24 || !props.left || !props.right; +	var small = !props.left || !props.right;  	return <div -		className={'selection ' + props.className} +		className={'selection ' + (props.className || '')}  		style={{  			width: `calc(var(--zoom) * ${props.frameWidth} * 1px + 12px + ${props.widthOffset} * 1px)`,  			height: props.height,  			'--corner-size': small ? '6px' : '12px', +			'--visibility': props.visibility,  		} as CSSProperties}  	>  		<div className='background fill left posabs dispinbl l0' /> diff --git a/pages/editor.tsx b/pages/editor.tsx index 872e9f7..635934e 100644 --- a/pages/editor.tsx +++ b/pages/editor.tsx @@ -195,6 +195,7 @@ interface selectionPos {  	frameWidth: number;  	startOffset: number;  	widthOffset: number; +	visibility: number;  }  var selectionPos: SpringValues<selectionPos>,  	selectionPosAPI: SpringRef<selectionPos>; @@ -211,6 +212,7 @@ function select(slides: anySlide[]) {  			},  		});  		setSetting('default'); +		selectionPosAPI({ visibility: 0 });  	} else {  		var left = slides[0];  		var right = slides[slides.length - 1]; @@ -237,6 +239,7 @@ function select(slides: anySlide[]) {  			},  		});  		setSetting('slide'); +		selectionPosAPI({ visibility: 1 });  	}  } @@ -324,6 +327,9 @@ function TimelineKeyframe(props: {  				modifySlide({ frame: frame + endOffset });  				modifySlide({ beginFrame: frame + startOffset });  			} +			var end = props.slide; +			var begin = new loopBeginSlide(end as loopSlide); +			select([begin, end]);  		} else {  			if (intentional) {  				api.start({ frame }); @@ -339,22 +345,24 @@ function TimelineKeyframe(props: {  	if (props.slide.type == 'loop') {  		// loop start -		useDrag(({ xy: [x, _y] }) => { -			var frame = Math.max(0, Math.round(getFrameAtOffset(x - 240, timelineZoom.value)) - 1); - -			api.start({ begin: frame }); - -			modifySlide({ beginFrame: frame }); -		}, { domTarget: loopStartRef, eventOptions: { passive: false } }); +		useDrag(({ xy: [x, _y], intentional }) => { +			if (intentional) { +				var frame = Math.max(0, Math.round(getFrameAtOffset(x - 240, timelineZoom.value)) - 1); +				api.start({ begin: frame }); +				modifySlide({ beginFrame: frame }); +			} +			select([new loopBeginSlide(props.slide as loopSlide)]); +		}, { domTarget: loopStartRef, eventOptions: { passive: false }, threshold: 10, triggerAllEvents: true });  		// loop end -		useDrag(({ xy: [x, _y] }) => { -			var frame = Math.max(0, Math.round(getFrameAtOffset(x - 240, timelineZoom.value)) - 1); - -			api.start({ frame }); - -			modifySlide({ frame }); -		}, { domTarget: loopEndRef, eventOptions: { passive: false } }); +		useDrag(({ xy: [x, _y], intentional }) => { +			if (intentional) { +				var frame = Math.max(0, Math.round(getFrameAtOffset(x - 240, timelineZoom.value)) - 1); +				api.start({ frame }); +				modifySlide({ frame }); +			} +			select([props.slide]); +		}, { domTarget: loopEndRef, eventOptions: { passive: false }, threshold: 10, triggerAllEvents: true });  	}  	var mouseUpListener = useRef(null); @@ -399,9 +407,6 @@ function TimelineLabels() {  }  function TimelineSelection(props: { selectionDragArea: Ref<ReactNode>; }) { -	var workingTimeline = useHookstate(global).timeline.workingTimeline; -	var tool = useHookstate(global).timeline.tool; -  	var selection = useHookstate(global).selection;  	[selectionPos, selectionPosAPI] = useSpring(() => ({ @@ -414,6 +419,7 @@ function TimelineSelection(props: { selectionDragArea: Ref<ReactNode>; }) {  		frameWidth: 0,  		startOffset: 0,  		widthOffset: 0, +		visibility: 0,  		config: { mass: 0.5, tension: 500, friction: 20 },  	})); @@ -428,7 +434,7 @@ function TimelineSelection(props: { selectionDragArea: Ref<ReactNode>; }) {  			switch (slide.value.type as slideTypes | 'loopBegin') {  				case 'loopBegin': {  					if (!api) break; -					var loop = workingTimeline.value.find(s => s.id == slide.value.id) as loopSlide; +					var loop = global.timeline.workingTimeline.value.find(s => s.id == slide.value.id) as loopSlide;  					var begin = loop.beginFrame + frameOffset;  					api.start({ begin }); @@ -446,7 +452,7 @@ function TimelineSelection(props: { selectionDragArea: Ref<ReactNode>; }) {  					api.start({ frame });  					if (last) { -						workingTimeline.find(s => s.value.id == slide.value.id).frame.set(frame); +						global.timeline.workingTimeline.find(s => s.value.id == slide.value.id).frame.set(frame);  						global.update.refreshLiveTimeline.value();  					}  				} @@ -458,11 +464,14 @@ function TimelineSelection(props: { selectionDragArea: Ref<ReactNode>; }) {  	}, { domTarget: selectionRef, eventOptions: { passive: false } });  	useDrag(({ xy: [x, y], initial: [bx, by], first, last, movement: [ox, oy] }) => { -		if (tool.value != 'cursor') return; +		if (global.timeline.tool.value != 'cursor') return;  		var minDistance = 5; // minimal drag distance in pixels to register selection  		var distanceTraveled = Math.sqrt(ox ** 2 + oy ** 2); -		if (global.selection.hidden.value && distanceTraveled > minDistance) global.selection.hidden.set(false); +		if (global.selection.hidden.value && distanceTraveled > minDistance) { +			global.selection.hidden.set(false); +			selectionPosAPI.start({ visibility: 1 }); +		}  		if (global.selection.type.left.value) global.selection.type.left.set(null);  		if (global.selection.type.right.value) global.selection.type.right.set(null);  		if (global.selection.placed.value) global.selection.placed.set(false); @@ -492,7 +501,7 @@ function TimelineSelection(props: { selectionDragArea: Ref<ReactNode>; }) {  		var frameWidth = Math.abs(sx) / zoom;  		var startingFrame = x1 / zoom; -		selectionPosAPI[first && global.selection.hidden ? 'set' : 'start']({ +		selectionPosAPI[first && global.selection.hidden.value ? 'set' : 'start']({  			x1,  			y1,  			x2, @@ -506,15 +515,14 @@ function TimelineSelection(props: { selectionDragArea: Ref<ReactNode>; }) {  			if (distanceTraveled <= minDistance) {  				setSetting('default');  				global.selection.hidden.set(true); +				selectionPosAPI.start({ visibility: 0 });  			} else {  				var endingFrame = startingFrame + frameWidth;  				var expandedTimeline = new Array(...project.timeline.slides.value);  				for (let i = 0; i < expandedTimeline.length; i++) {  					var slide = expandedTimeline[i];  					if (slide.type != 'loop') continue; -					var beginFrame = (slide as loopSlide).beginFrame; -					expandedTimeline.splice(i, 0, new loopBeginSlide(beginFrame)); -					expandedTimeline[i].id = expandedTimeline[i + 1].id; +					expandedTimeline.splice(i, 0, new loopBeginSlide(slide as loopSlide));  					i++;  				} @@ -530,10 +538,10 @@ function TimelineSelection(props: { selectionDragArea: Ref<ReactNode>; }) {  	useMousetrap(['del', 'backspace'], () => {  		if (!global.selection.placed) return; -		var selection = global.selection.slides.attach(Downgraded).value +		var sel = global.selection.slides.attach(Downgraded).value  			.map(s => ({ id: s.id.toString(), type: s.type.toString() }))  			.filter(s => slideTypes.includes(s.type)); -		selection.forEach(slide => global.timeline.workingTimeline.find(s => s.value?.id == slide.id).set(none)); +		sel.forEach(slide => global.timeline.workingTimeline.find(s => s.value?.id == slide.id).set(none));  		global.update.refreshLiveTimeline.value();  		setSetting('default'); @@ -542,6 +550,7 @@ function TimelineSelection(props: { selectionDragArea: Ref<ReactNode>; }) {  			hidden: true,  			slides: [],  		}); +		selectionPosAPI.start({ visibility: 0 });  	});  	function CustomSelection(props: { @@ -551,6 +560,7 @@ function TimelineSelection(props: { selectionDragArea: Ref<ReactNode>; }) {  		y2: number;  		widthOffset: number;  		frameWidth: number; +		visibility: number;  		className: string;  	}) {  		return <Selection @@ -561,6 +571,7 @@ function TimelineSelection(props: { selectionDragArea: Ref<ReactNode>; }) {  			left={selection.type.left.get()}  			right={selection.type.right.get()}  			widthOffset={props.widthOffset} +			visibility={props.visibility}  		/>;  	}  	var AnimatedSelection = animated(props => <CustomSelection {...props} />); @@ -584,6 +595,7 @@ function TimelineSelection(props: { selectionDragArea: Ref<ReactNode>; }) {  			y2={selectionPos.y2}  			widthOffset={selectionPos.widthOffset}  			frameWidth={selectionPos.frameWidth} +			visibility={selectionPos.visibility}  			className={'' + (selection.active.get() ? 'active ' : '') + (selection.hidden.get() ? 'hidden ' : '')}  		/>  	</animated.div>; diff --git a/styles/selection.css b/styles/selection.css index 6e16cee..a139367 100644 --- a/styles/selection.css +++ b/styles/selection.css @@ -2,8 +2,8 @@  	--selection-color: var(--gruble);  	--corner-size: 12px;  	filter: drop-shadow(0px 0px 16px var(--selection-color)); -	transition-duration: 100ms; -	transition-property: transform, opacity; +	transform: scale(calc(0.5 * var(--visibility) + 0.5)); +	opacity: var(--visibility);  }  .selection .bar.top, @@ -57,7 +57,3 @@  	opacity: .15;  } -.selection.hidden { -	transform: scale(0.5); -	opacity: 0; -} diff --git a/timeline.ts b/timeline.ts index 7f673c5..f5f8066 100644 --- a/timeline.ts +++ b/timeline.ts @@ -32,6 +32,11 @@ export class loopSlide extends slide {  export class loopBeginSlide extends slide {  	type = 'loopBegin' as slideTypes; + +	constructor(public parent: loopSlide) { +		super(parent.beginFrame); +		this.id = parent.id; +	}  }  export var toolToSlide = { |