Slider.tsx 8.7 KB

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