22 <div class =" derivate-pane" >
33 <s-tab mode =" fixed" v-model.lazy =" tab" >
44 <s-tab-item value =" derivate" >
5- <div slot =" text" >切线设置</div >
5+ <div slot =" text" class =" tab-text" >
6+ 切线设置
7+ <Transition name =" badge" >
8+ <s-badge class =" tab-badge" v-if =" derivateEnabled" ></s-badge >
9+ </Transition >
10+ </div >
611 </s-tab-item >
712 <s-tab-item value =" secant" >
8- <div slot =" text" >割线设置</div >
13+ <div slot =" text" class =" tab-text" >
14+ 割线设置
15+ <Transition name =" badge" >
16+ <s-badge class =" tab-badge" v-if =" secantEnabled" ></s-badge >
17+ </Transition >
18+ </div >
919 </s-tab-item >
1020 </s-tab >
1121
1222 <!-- derivate -->
13- <div class =" content" v-if =" tab === 'derivate'" >
14- <div class =" field" >
23+ <div
24+ class =" content"
25+ v-if =" tab === 'derivate'"
26+ :class =" { enabled: derivateEnabled }"
27+ >
28+ <div class =" field no-opacity" >
1529 <div class =" label" >启用切线显示</div >
1630 <s-switch type =" checkbox" v-model.lazy =" derivateEnabled" ></s-switch >
1731 </div >
1832 <div class =" field main-fn" >
19- <span class =" label styled" >y'=</span >
33+ <span class =" label wide styled" >y'=</span >
2034 <FunctionField class =" styled fn" label =" f'(x)" v-model =" derivateFn" />
2135 </div >
2236 <div class =" field" >
2337 <div class =" label" >切点位置</div >
24- <s-segmented-button value =" b" >
25- <s-segmented-button-item value =" a" > 指定切点 </s-segmented-button-item >
26- <s-segmented-button-item value =" b" > 跟随鼠标 </s-segmented-button-item >
38+ <s-segmented-button v-model.lazy =" derivFollowMouseStr" >
39+ <s-segmented-button-item value =" followMouse" >
40+ 跟随鼠标
41+ </s-segmented-button-item >
42+ <s-segmented-button-item value =" manual" >
43+ 指定切点
44+ </s-segmented-button-item >
2745 </s-segmented-button >
2846 </div >
29- <div class =" field" >
30- <div class =" label" >切点横坐标</div >
31- <s-text-field type =" number" label =" x" class =" styled" ></s-text-field >
32- </div >
47+ <s-fold :folded =" derivFollowMouse" class =" derivate-x-coord-fold" >
48+ <div class =" field x-coord-field" >
49+ <div class =" label" >切点横坐标</div >
50+ <s-text-field
51+ class =" x-coord styled"
52+ type =" number"
53+ label =" x"
54+ v-model =" derivateX"
55+ ></s-text-field >
56+ </div >
57+ </s-fold >
3358 </div >
3459
3560 <!-- secant -->
36- <div class =" content" v-else >
37- <div class =" field" >
61+ <div class =" content" v-else :class = " { enabled: secantEnabled } " >
62+ <div class =" field no-opacity " >
3863 <div class =" label" >启用割线显示</div >
3964 <s-switch type =" checkbox" v-model.lazy =" secantEnabled" ></s-switch >
4065 </div >
41-
66+ <div class =" secants" >
67+ <AnimatedList >
68+ <AnimatedListItem v-for =" (item, index) in secantArr" :key =" item.key" >
69+ <div class =" secant-item" >
70+ <s-checkbox
71+ type =" checkbox"
72+ v-model =" item.followMouse"
73+ class =" secant-checkbox"
74+ >
75+ 跟随鼠标
76+ </s-checkbox >
77+ <s-text-field
78+ class =" sec-coord styled-inner"
79+ type =" number"
80+ label =" x₁"
81+ v-model =" item.x1"
82+ ></s-text-field >
83+ <Transition name =" field-squeeze" >
84+ <s-text-field
85+ v-if =" !item.followMouse"
86+ class =" sec-coord styled-inner"
87+ type =" number"
88+ label =" x₂"
89+ v-model =" item.x2"
90+ ></s-text-field >
91+ </Transition >
92+ <s-icon-button
93+ @click =" secantArr.splice(index, 1)"
94+ class =" secant-delete"
95+ >
96+ <SIconDelete />
97+ </s-icon-button >
98+ </div >
99+ </AnimatedListItem >
100+ </AnimatedList >
101+ </div >
102+ <s-button
103+ class =" add-secant"
104+ type =" text"
105+ @click ="
106+ secantArr.push({
107+ key: Math.random(),
108+ followMouse: true,
109+ x1: 0,
110+ x2: '',
111+ })
112+ "
113+ >
114+ <s-icon slot =" start" name =" add" /> 添加
115+ </s-button >
42116 </div >
43117 </div >
44118</template >
45119
46120<script lang="ts" setup>
47121import { PrivateDataTypes } from " @/types/data" ;
48- import { ref , toRef } from " vue" ;
122+ import { computed , reactive , ref , toRef } from " vue" ;
49123import FunctionField from " ./function.vue" ;
50124
125+ import AnimatedList from " @/ui/animated/animatedList.vue" ;
126+ import AnimatedListItem from " @/ui/animated/animatedListItem.vue" ;
127+ import SIconDelete from " @/ui/icons/delete.vue" ;
128+
51129const props = defineProps <{
52130 self: PrivateDataTypes .Linear ;
53131}>();
@@ -57,28 +135,143 @@ const self = toRef(props, "self");
57135
58136const derivateEnabled = ref (false );
59137const derivateFn = ref (" " );
138+ const derivFollowMouseStr = ref (" followMouse" );
139+ const derivFollowMouse = computed (
140+ () => derivFollowMouseStr .value === " followMouse"
141+ );
142+ const derivateX = ref (0 );
143+
60144const secantEnabled = ref (false );
145+ const secantArr = reactive <
146+ {
147+ key : number ;
148+ followMouse : boolean ;
149+ x1 : number | " " ;
150+ x2 : number | " " ;
151+ }[]
152+ > ([]);
61153 </script >
62154
63155<style lang="scss">
64156.derivate-pane {
65157 width : 25em ;
158+ s-tab {
159+ background-color : var (--s-color-surface-container-low );
160+ }
161+ .tab-text {
162+ display : flex ;
163+ align-items : center ;
164+ }
165+ .tab-badge {
166+ background-color : var (--s-color-success );
167+ color : var (--s-color-on-success );
168+ margin-left : 0.8em ;
169+ }
170+ .badge {
171+ & -enter-active ,
172+ & -leave-active {
173+ transition :
174+ opacity 0.1s ,
175+ margin-left 0.5s cubic-bezier (0 , 1 , 0 , 1 );
176+ }
177+ & -leave-to ,
178+ & -enter-from {
179+ opacity : 0 ;
180+ margin-left : -8px ;
181+ }
182+ }
66183 .content {
67- padding : 15 px ;
184+ padding : 20 px ;
68185 display : flex ;
69186 flex-direction : column ;
70- gap : 0.5em ;
187+ gap : 0.8em ;
188+ & .enabled {
189+ .field ,
190+ .secants ,
191+ .add-secant {
192+ opacity : 1 ;
193+ }
194+ }
195+ .field ,
196+ .secants ,
197+ .add-secant {
198+ opacity : 0.5 ;
199+ transition : opacity 0.2s ;
200+ }
71201 }
72202 .field {
73203 display : flex ;
74204 justify-content : space-between ;
75205 align-items : center ;
76206 }
207+ .no-opacity {
208+ opacity : 1 !important ;
209+ }
77210 .field.main-fn {
78211 font-size : 1.5em ;
79- .label {
212+ .label.wide {
80213 width : 2.2em ;
81214 }
82215 }
216+ .x-coord.styled {
217+ font-size : 20px ;
218+ max-width : 10em ;
219+ }
220+ .derivate-x-coord-fold {
221+ margin-top : -0.8em ;
222+ .x-coord-field {
223+ margin-top : 0.8em ;
224+ }
225+ }
226+ .secants {
227+ display : flex ;
228+ flex-direction : column ;
229+ margin : 0 -0.5em 0 -1em ;
230+ }
231+ .secant-item {
232+ display : flex ;
233+ align-items : center ;
234+ margin : 0.3em 0 ;
235+ padding-left : 1em ;
236+ gap : 0.5em ;
237+ }
238+ .sec-coord {
239+ width : 0 ;
240+ flex-grow : 1 ;
241+ font-size : 20px !important ;
242+ }
243+ .secant-delete {
244+ flex-shrink : 0 ;
245+ margin-left : -0.2em ;
246+ }
247+ .secant-checkbox {
248+ margin-right : 0.3em ;
249+ }
250+ .field-squeeze {
251+ & -enter-active ,
252+ & -leave-active {
253+ transition :
254+ flex-grow 0.3s cubic-bezier (0.075 , 0.82 , 0.165 , 1 ),
255+ width 0.3s cubic-bezier (0.075 , 0.82 , 0.165 , 1 ),
256+ margin-left 0.35s 0.05s ;
257+ min-width : 0 ;
258+ overflow-x : hidden ;
259+ }
260+ & -enter-active {
261+ transition :
262+ flex-grow 0.3s var (--s-motion-easing-emphasized ),
263+ width 0.3s var (--s-motion-easing-emphasized ),
264+ margin-left 0.05s ;
265+ }
266+ & -enter-from ,
267+ & -leave-to {
268+ flex-grow : 0 !important ;
269+ width : 0 !important ;
270+ margin-left : -0.5em ;
271+ }
272+ }
273+ .add-secant {
274+ margin-top : -0.3em ;
275+ }
83276}
84277 </style >
0 commit comments