零基础涂鸦智能面板SDK开发记录(2/4)

前言

大概花了三天时间看了下相关知识,在这先吧能看懂的跟大家聊一聊。
这是我在github上找的demo     https://github.com/TuyaInc/tuya-panel-kit-template
以其中的examples\lampClassic为例子。

分析

接上篇分析main.js可以分析出启动页(首页),下列为首页代码

import HomeBottomView from './bottom';
import Lamp from '../lamp';

const HomeScene = () => (
  <View style={styles.container}>
    <Lamp />
    <HomeBottomView />
  </View> 
);
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});

export default HomeScene;

从上分析得由灯和底部按钮组成。我们现在进入lamp目录。以下是我了解的

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { View, StyleSheet, Image, TouchableWithoutFeedback } from 'react-native';
import { Utils, TYText } from 'tuya-panel-kit';
import { updateDp } from '../../redux/actions/common';
import { updateCloud } from '../../redux/actions/cloud';
import Config from '../../config';
import Strings from '../../i18n';
import LampInstance from '../../utils/LampInstance';
import { WORKMODE } from '../../utils/constant';
import { syncThrottle, handleFifthSceneColor } from '../../utils';
import Color from '../../utils/color';
import HuePicker from '../../components/HuePicker';
import resource from '../../res';


const { convertX: cx } = Utils.RatioUtils;

//React Native自行参考https://www.react-native.cn/docs/getting-started

class Lamp extends Component {

  //先得知道React的State和Props是啥玩意
  //propTypes:用来检测props数据类型的变量,包括基本类型的的字符串,布尔值,数字,以及引用类型的对象,数组,函数,甚至还有ES6新增的符号类型
  //isRequired表示必传参数
  static propTypes = {
    power: PropTypes.bool.isRequired,
    workMode: PropTypes.string.isRequired,
    brightness: PropTypes.number.isRequired,
    kelvin: PropTypes.number.isRequired,
    colour: PropTypes.string.isRequired,
    sceneValue: PropTypes.string.isRequired,
    selectSceneColorIndex: PropTypes.number.isRequired,
    isEditSceneColor: PropTypes.bool.isRequired,
    isEditMode: PropTypes.bool.isRequired,
    updateDp: PropTypes.func.isRequired,
    selectSceneId: PropTypes.number.isRequired,
    scenes: PropTypes.array.isRequired,
    updateCloud: PropTypes.func.isRequired,
  };

  constructor(props) {
    super(props);
    //来自utils目录猜测是调节灯光
    LampInstance.setInstance(this);
    //这玩意看不懂的参考这https://www.runoob.com/react/react-props.html(State 和 Props)
    this.state = this.initData(this.props);
  }

  // 生命周期的方法有:
  // componentWillMount 在渲染前调用,在客户端也在服务端。
  // componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 
  // 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。
  // componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
  // shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
  // 可以在你确认不需要更新组件时使用。
  // componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
  // componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。
  // componentWillUnmount在组件从 DOM 中移除之前立刻被调用。
  componentWillReceiveProps(nextProps) {
    this.setState({
      //在react中,会看到{...this.props}的代码,不知道是什么意思,现在记录一下。
      //{...this.props}是props所提供的语法糖,可以将父组件的所有属性复制给子组件
      ...this.initData(nextProps),
    });
  }

  setLightColor(color, brightness) {
    // 灯亮度效果由于1%-100%显示比较差,对应显示8%-100%范围
    this.shadowRef.setNativeProps({
      style: { opacity: this.formatOpacity(brightness) },
    });
    this.ligthRef.setNativeProps({
      style: { tintColor: color },
    });
  }

  // 灯亮度效果由于1%-100%显示比较差,对应显示8%-100%范围
  formatOpacity(brightness) {
    return 0.08 + ((brightness - 10) / (1000 - 10)) * (1 - 0.08);
  }

  initData(props) {
    const {
      workMode,
      brightness,
      kelvin,
      colour,
      sceneValue,
      isEditSceneColor,
      scenes,
      selectSceneId,
      selectSceneColorIndex,
    } = props;
    let currentColor = '#fff';
    let currentBright = 1000;
    let hsv = [0, 0, 0];
    switch (workMode) {
      case WORKMODE.WHITE:
        currentColor = Color.brightKelvin2rgb(1000, kelvin);
        currentBright = brightness;
        break;
      case WORKMODE.COLOUR:
        hsv = Color.decodeColourData(colour);
        currentColor = Color.hsv2hex(hsv[0], hsv[1], 1000);
        currentBright = hsv[2];
        break;
      case WORKMODE.SCENE: {
        let hsvbk;
        if (isEditSceneColor) {
          const exist = scenes.find(item => item.sceneId === selectSceneId);
          if (exist) {
            const [, , , ...hsvbks] = Color.decodeSceneValue(exist.value);
            hsvbk = hsvbks[selectSceneColorIndex];
          }
        } else {
          const [, , , ...hsvbks] = Color.decodeSceneValue(sceneValue);
          [hsvbk] = hsvbks;
        }
        if (!hsvbk) {
          hsvbk = [0, 0, 0, 0, 0];
        }
        hsv = hsvbk;
        // 取第一组数据
        const [h, s, v, b, k] = hsvbk;
        // 白光
        if (b || k) {
          currentColor = Color.brightKelvin2rgb(1000, k);
          currentBright = b;
        } else {
          currentColor = Color.hsv2hex(h, s, 1000);
          currentBright = v;
        }
        break;
      }
      default:
        break;
    }

    // 标题
    const modeTitle = Strings.getLang(`mode_${workMode}`);
    return {
      modeTitle,
      hsv,
      currentColor,
      currentBright,
    };
  }

  handleChangePower = () => {
    const { power } = this.props;
    this.props.updateDp({
      [Config.dpCodes.power]: !power,
    });
  };

  handleHueChange = syncThrottle(
    hue => {
      const { hsv } = this.state;
      const currentColor = Color.hsv2hex(hue, hsv[1], 1000);
      this.setLightColor(currentColor, hsv[2]);
    },
    hue => {
      const { hsv } = this.state;
      const editHsv = [hue, hsv[1], hsv[2]];
      const currentColor = Color.encodeColourControlData(...editHsv);
      this.props.updateDp({
        [Config.dpCodes.controlData]: currentColor,
      });
    }
  );

  hangleHueChangeCompelete = hue => {
    this.handleHueChange.cancel();
    const { isEditSceneColor, selectSceneId, scenes, selectSceneColorIndex } = this.props;
    const { hsv } = this.state;
    if (isEditSceneColor) {
      const exist = scenes.find(item => item.sceneId === selectSceneId);
      if (exist) {
        const [num, speed, mode, ...hsvbks] = Color.decodeSceneValue(exist.value);
        // 为了与v1版本一致,如果是第5个场景,则只显示一个颜色,并根据用户选择了颜色处理成亮暗两种颜色
        const isFifth = num === 4;
        if (selectSceneColorIndex < hsvbks.length) {
          let newHsvbks = hsvbks;
          if (isFifth) {
            const [h, s, v] = hsvbks[0];
            newHsvbks = handleFifthSceneColor(hue, s, v);
          } else {
            newHsvbks[selectSceneColorIndex][0] = hue;
          }

          const value = Color.encodeSceneValue([num, speed, mode, ...newHsvbks]);
          this.props.updateDp({
            [Config.dpCodes.sceneData]: value,
          });
          this.props.updateCloud(`scene_${+num}`, { sceneId: exist.sceneId, value });
        }
      }
    } else {
      this.props.updateDp({
        [Config.dpCodes.colourData]: Color.encodeColourData(hue, hsv[1], hsv[2]),
      });
    }
    this.setState({
      hsv: [hue, hsv[1], hsv[2]],
    });
  };

  //这块是用来画UI的,和触发事件调用方法
  render() {
    const { power, isEditMode, isEditSceneColor, workMode } = this.props;
    const { currentColor, currentBright, modeTitle } = this.state;
    const isShowHue = power && isEditMode && (workMode === WORKMODE.COLOUR || isEditSceneColor);

    const lampOnImage = resource.lightOn;
    const lampOffImage = resource.lightOff;

    return (
      <View style={styles.container} accessibilityLabel="LampView">
        {power && (
          <TYText accessibilityLabel="Light_Mode_Name" style={[styles.title]}>
            {modeTitle}
          </TYText>
        )}
        <View style={styles.box}>
          <Image
            ref={ref => (this.shadowRef = ref)}
            source={resource.lightShadow}
            style={[
              styles.lightShadow,
              { opacity: power ? this.formatOpacity(currentBright) : 0.2 },
            ]}
          />
          <TYText style={[styles.powerTip, { opacity: power ? 0 : 1 }]}>
            {Strings.getLang('power_tip')}
          </TYText>
          <HuePicker
            style={[styles.huePicker, { opacity: isShowHue ? 1 : 0 }]}
            hue={isShowHue ? this.state.hsv[0] : 0}
            touchThumbRadius={cx(25)}
            touchOffset={cx(8)}
            onChange={this.handleHueChange}
            onRelease={this.hangleHueChangeCompelete}
            onPress={this.hangleHueChangeCompelete}
            disabled={!isShowHue}
          />
          <TouchableWithoutFeedback
            onPress={this.handleChangePower}
            accessibilityLabel="Light_Btn_Open"
          >
            <View style={styles.lightBtn}>
              <Image
                ref={ref => (this.ligthRef = ref)}
                source={power ? lampOnImage : lampOffImage}
                style={[
                  styles.light,
                  { tintColor: power ? currentColor : 'rgba(255,255,255,0.4)' },
                ]}
              />
            </View>
          </TouchableWithoutFeedback>
        </View>
      </View>
    );
  }
}

//React Native 中用来集中定义组件的样式
const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  title: {
    width: '100%',
    position: 'absolute',
    top: cx(10),
    backgroundColor: 'transparent',
    fontSize: cx(14),
    textAlign: 'center',
  },
  box: {
    width: cx(374),
    height: cx(374),
    alignItems: 'center',
    justifyContent: 'center',
  },
  lightShadow: {
    width: cx(283),
    height: cx(284),
  },
  lightBtn: {
    position: 'absolute',
    width: cx(120),
    height: cx(120),
    top: cx(127),
    left: cx(127),
    zIndex: 1,
  },
  light: {
    width: cx(120),
    height: cx(120),
  },
  powerTip: {
    position: 'absolute',
    width: '100%',
    bottom: cx(30),
    left: 0,
    textAlign: 'center',
    fontSize: cx(12),
    color: '#fff',
    backgroundColor: 'transparent',
  },
  huePicker: {
    position: 'absolute',
    left: cx(49.5),
    top: cx(49.5),
  },
});


//React-Redux提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。
export default connect(
  ({ dpState, cloudState }) => {
    const {
      dpCodes: {
        power: powerCode,
        workMode: workModeCode,
        bright: brightCode,
        kelvin: kelvinCode,
        colourData: colourDataCode,
        sceneData: sceneDataCode,
      },
    } = Config;
    return {
      power: dpState[powerCode],
      workMode: dpState[workModeCode],
      brightness: Reflect.has(dpState, brightCode) ? dpState[brightCode] : 1000,
      kelvin: Reflect.has(dpState, kelvinCode) ? dpState[kelvinCode] : 1000,
      colour: dpState[colourDataCode],
      sceneValue: dpState[sceneDataCode],
      isEditMode: cloudState.isEditMode,
      isEditSceneColor: cloudState.isEditSceneColor,
      selectSceneColorIndex: cloudState.selectSceneColorIndex,
      selectSceneId: cloudState.selectSceneId,
      scenes: cloudState.scenes,
    };
  },
  dispatch => ({
    updateDp: updateDp(dispatch),
    updateCloud: updateCloud(dispatch),
  })
)(Lamp);

大致知道了每个函数的用途,但实际使用还是无从下手。

零基础涂鸦智能面板SDK开发记录(4/4)-CSDN博客
零基础涂鸦智能面板SDK开发记录(3/4)-CSDN博客
零基础涂鸦智能面板SDK开发记录(1/4)_小铁-Android的博客-CSDN博客 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值