Slider.tsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import { View, Text, Image } from '@tarojs/components'
  2. import './Slider.scss'
  3. import { ColorType } from '@/context/themes/color'
  4. import { useEffect, useRef, useState } from 'react';
  5. import { rpxToPx } from '@/utils/tools';
  6. import Taro from '@tarojs/taro';
  7. import { useSelector } from 'react-redux';
  8. import { useTranslation } from 'react-i18next';
  9. var currentValue = 0;
  10. export default function (props: { onChanged?: Function, value?: number, edit?: boolean, isPreMeal?: boolean, id?: string }) {
  11. const sliderWidth = 578;
  12. const [brightness, setBrightness] = useState(props.value ? props.value * 10 : 0);
  13. const [isSliding, setIsSliding] = useState(false);
  14. const [value, setValue] = useState(props.value ? props.value : 0)
  15. const [startX, setStartX] = useState(0);
  16. const common = useSelector((state: any) => state.common);
  17. const { t } = useTranslation()
  18. useEffect(() => {
  19. // if (props.value) {
  20. // setBrightness(props.value * 10)
  21. // }
  22. }, [props.value])
  23. const handleTouchStart = (event) => {
  24. setIsSliding(true);
  25. setStartX(event.touches[0].clientX);
  26. };
  27. const handleTouchMove = (event) => {
  28. if (isSliding) {
  29. const { touches } = event;
  30. const touchX = touches[0].clientX;
  31. const deltaX = touchX - startX;
  32. // console.log(deltaY,startY,touchY)
  33. // const containerHeight = rpxToPx(182); // 容器高度,根据实际情况调整
  34. let brightnessValue = brightness + deltaX / rpxToPx(sliderWidth - 120) * 100;// / containerHeight * 100;
  35. // 边界限制
  36. brightnessValue = Math.max(0, Math.min(brightnessValue, 100));
  37. brightnessValue = Math.round(brightnessValue)
  38. setStartX(touchX)
  39. setBrightness(brightnessValue);
  40. currentValue = brightnessValue;
  41. setValue(calculateNumber(brightnessValue))
  42. }
  43. };
  44. const handleTouchEnd = () => {
  45. setIsSliding(false);
  46. if (props.onChanged) {
  47. var obj = common.food_scales.filter((item: any) => {
  48. return item.point == value
  49. })
  50. Taro.showModal({
  51. title: props.isPreMeal ? t('feature.food.pre_meal_confirm_title') : t('feature.food.post_meal_confirm_title'),
  52. content: value + '分 - ' + obj[0].description ?? '暂无描述',
  53. success: function (res) {
  54. if (res.confirm) {
  55. props.onChanged && props.onChanged(value);
  56. }
  57. else {
  58. if (props.isPreMeal) {
  59. setStartX(0);
  60. setBrightness(0)
  61. setValue(0)
  62. }
  63. else {
  64. setBrightness(props.value! * 10)
  65. setValue(props.value!)
  66. }
  67. }
  68. }
  69. })
  70. }
  71. if (props.edit) {
  72. if (startX != 0 && value != 0) {
  73. // 松手后,滑块动画移动到最左边
  74. Taro.vibrateShort({
  75. type: 'heavy'
  76. })
  77. // Taro.showToast({
  78. // icon: 'none',
  79. // title: '请先上传!'
  80. // })
  81. Taro.showModal({
  82. title: '提示',
  83. content: t('feature.food.pre_meal_enforce_order_alert'),
  84. showCancel: false,
  85. confirmText: '好的',
  86. success: function (res) {
  87. if (res.confirm) {
  88. setStartX(0);
  89. setBrightness(0)
  90. setValue(0)
  91. }
  92. }
  93. })
  94. // const animationId = requestAnimationFrame(moveToStart);
  95. // return () => cancelAnimationFrame(animationId);
  96. }
  97. }
  98. };
  99. const moveToStart = () => {
  100. const newPosition = Math.max(currentValue - 8, 0);
  101. currentValue = newPosition
  102. setBrightness(newPosition)
  103. setValue(calculateNumber(newPosition))
  104. if (newPosition > 0) {
  105. requestAnimationFrame(moveToStart);
  106. } else {
  107. setStartX(0);
  108. setBrightness(0)
  109. }
  110. };
  111. function calculateOpacity(number) {
  112. if (number === 0 || number === 100) {
  113. return 1;
  114. } else if (number === 50) {
  115. return 0;
  116. } else if (number < 50) {
  117. return 1 - (number / 50);
  118. } else {
  119. return (number - 50) / 50;
  120. }
  121. }
  122. function calculateNumber(number) {
  123. if (number <= 2) {
  124. return 0;
  125. } else if (number <= 7) {
  126. return 0.5;
  127. } else {
  128. var result = Math.floor(number / 10);
  129. var decimal = number % 10;
  130. if (decimal >= 3 && decimal <= 7) {
  131. return result + 0.5;
  132. } else if (decimal >= 8 && decimal <= 9) {
  133. if (result == 9) {
  134. return 9.5
  135. }
  136. return result + 1;
  137. } else {
  138. return result;
  139. }
  140. }
  141. }
  142. function getTitle() {
  143. var obj = common.food_scales.filter((item: any) => {
  144. return item.point == value
  145. })
  146. return obj[0].name
  147. }
  148. function getDesc() {
  149. var obj = common.food_scales.filter((item: any) => {
  150. return item.point == value
  151. })
  152. return obj[0].description ?? '暂无描述'
  153. }
  154. return <View style={{ display: 'flex', flexDirection: 'column', position: 'relative', width: rpxToPx(578) }}>
  155. <View className='slider-container'
  156. catchMove
  157. onTouchStart={handleTouchStart}
  158. onTouchMove={handleTouchMove}
  159. onTouchEnd={handleTouchEnd}>
  160. <View className='slider-tip-bg' style={{ width: (100 - brightness) * 0.01 * rpxToPx(sliderWidth - 120) + rpxToPx(28) }}>
  161. <Text className='slider-tip-title'>{props.isPreMeal || !props.onChanged ?
  162. t('feature.food.slider_tip_pre_meal_title') :
  163. t('feature.food.slider_tip_post_meal_title')}</Text>
  164. <Text className='slider-tip-desc'>{props.isPreMeal || !props.onChanged ?
  165. t('feature.food.slider_tip_pre_meal_desc') :
  166. t('feature.food.slider_tip_post_meal_desc')}</Text>
  167. </View>
  168. <View className='slider-item-bg' style={{ width: brightness * 0.01 * rpxToPx(sliderWidth - 120) + rpxToPx(112) }}>
  169. </View>
  170. <View className='slider-item' style={{
  171. width: brightness * 0.01 * rpxToPx(sliderWidth - 120) + rpxToPx(112),
  172. backgroundColor: brightness <= 50 ? ColorType.fast : ColorType.food,
  173. opacity: calculateOpacity(brightness),
  174. }}>
  175. </View>
  176. {
  177. isSliding || value != 0 ? <View className='slider-text-bg'>
  178. <Text className='slider-text'>{value}</Text>
  179. </View> :
  180. <View className='slider-arrow-bg'>
  181. <Image src={require('@assets/images/arrow4.png')} className='slider-arrow' />
  182. </View>
  183. }
  184. </View>
  185. {
  186. isSliding && <View className='tooltip_bg'>
  187. <View style={{ flex: 1 }} />
  188. <View className='tooltip_content'>
  189. <Text className='tooltip_title'>{getTitle()}</Text>
  190. <Text className='tooltip_desc'>{getDesc()}</Text>
  191. </View>
  192. <View className='tooltip_arrow' style={{ marginLeft: brightness * 0.01 * rpxToPx(sliderWidth - 120) + rpxToPx((120 - 50) / 2) }} />
  193. </View>
  194. }
  195. </View>
  196. }