2018-01-18 17:40:18 zj20142213 阅读数 2022
  • React服务器端渲染及Next.js实战

    Next.js是一个基于React的一个服务端渲染简约框架。它使用React语法,可以很好的实现代码的模块化,有利于代码的开发和维护。 介绍服务器端渲染原理,深入浅出的讲解nuxt框架的使用。掌握服务器端渲染原理,掌握vue服务器端渲染

    769 人正在学习 去看看 尹涛

最近学习了下D3和仿照别人的案例写了个小demo,遇到了一些问题,在这里记录下:
1.遇到的一些坑:
(1.)D3.js中V3和V4的版本差别很大,所以一定要注意版本;
(2.)引用D3.js的js库文件的时候,应该通过npm安装库依赖,虽然D3.js不咋出名,但是npm还是可以安装它,安装命令是:
npm安装:npm install d3
前端导入:import * as d3 from 'd3';

(3.)react中css的解析和导入:
一:如果是通过create-react-app创建的项目,可以直接通过命令:

import './styles.css';

导入,可以参考蚂蚁金服的样式导入和操作;
二:非create-react-app项目,可以参考文章:
http://echizen.github.io/tech/2017/03-09-css-in-react
2.demo的源码:
animSVG.js


import React, { Component } from 'react';
import * as SimulationData from './svgData';
import * as d3 from 'd3';
import './styles.css';
//需要注意的问题:v3和v4版本差别很大,需要高度注意;
class AnimSVG extends React.Component {

    render() {
        {/* 定义柱状图的颜色数组; */ }
        const colors = ["#39D1DE", "#1CDB62", "#1FE015", "#A7E51F", "#E3E312", "#EA6A1F", "#29A0E3"];
        {/* 画布大小 */ }
        var width = 400;
        var height = 400;

        {/* 在 body 里添加一个 SVG 画布 */ }
        var svg = d3.select("body")
            .append("svg")
            .attr("width", width)
            .attr("height", height);

        {/* 画布周边的空白 */ }
        var padding = { left: 30, right: 30, top: 20, bottom: 20 };

        {/* 定义一个数组 */ }
        var dataset = [10, 20, 30, 40, 33, 24, 12, 5];

        {/* x轴的比例尺 */ }
        var xScale = d3.scale.ordinal()
            .domain(d3.range(dataset.length))
            .rangeRoundBands([0, width - padding.left - padding.right]);

        {/* y轴的比例尺 */ }
        var yScale = d3.scale.linear()
            .domain([0, d3.max(dataset)])
            .range([height - padding.top - padding.bottom, 0]);

        {/* 定义x轴 */ }
        var xAxis = d3.svg.axis()
            .scale(xScale)
            .orient("bottom");

        {/* 定义y轴 */ }
        var yAxis = d3.svg.axis()
            .scale(yScale)
            .orient("left");

        {/* 矩形之间的空白间距的宽度; */ }
        var rectPadding = 4;

        //添加矩形元素
        var rects = svg.selectAll(".MyRect")
            .data(dataset)
            .enter()
            .append("rect")
            .attr("class", "MyRect")
            .attr("transform", "translate(" + padding.left + "," + padding.top + ")")
            .attr("x", function (d, i) {
                return xScale(i) + rectPadding / 2;
            })
            .attr("width", xScale.rangeBand() - rectPadding)
            .attr("y", function (d) {
                var min = yScale.domain()[0];
                return yScale(min);
            })
            .attr("height", function (d) {
                return 0;
            })
            .transition()
            .delay(function (d, i) {
                return i * 200;
            })
            .duration(2000)
            .ease("bounce")
            .style("fill", function (uselsssVO, index) {
                var currentColorIndex = index % (colors.length);
                return colors[currentColorIndex];
            })
            .attr("y", function (d) {
                return yScale(d);
            })
            .attr("height", function (d) {
                return height - padding.top - padding.bottom - yScale(d);
            });

        {/* 添加文字元素 */ }
        var texts = svg.selectAll(".MyText")
            .data(dataset)
            .enter()
            .append("text")
            .attr("class", "MyText")
            .attr("transform", "translate(" + padding.left + "," + padding.top + ")")
            .attr("x", function (d, i) {
                return xScale(i) + rectPadding / 2;
            })
            .attr("dx", function () {
                return (xScale.rangeBand() - rectPadding) / 2;
            })
            .attr("dy", function (d) {
                return 20;
            })
            .text(function (d) {
                return d;
            })
            .attr("y", function (d) {
                var min = yScale.domain()[0];
                return yScale(min);
            })
            .transition()
            .delay(function (d, i) {
                return i * 200;
            })
            .duration(2000)
            .ease("bounce")
            .attr("y", function (d) {
                return yScale(d);
            });

        {/* 添加x轴 */ }
        svg.append("g")
            .attr("class", "axis")
            .attr("transform", "translate(" + padding.left + "," + (height - padding.bottom) + ")")
            .call(xAxis); 

        {/* 添加y轴 */ }
        svg.append("g")
            .attr("class", "axis")
            .attr("transform", "translate(" + padding.left + "," + padding.top + ")")
            .call(yAxis);

        return (
            <svg xmlns="http://www.w3.org/2000/svg"></svg>
        );
    }
}

export default AnimSVG;

styles.css

.axis path,
.axis line {
    fill: none;
    stroke: black;
    shape-rendering: crispEdges;
}

.axis text {
    font-family: sans-serif;
    font-size: 11px;
}

.MyRect {
    fill: steelblue;
}

.MyText {
    fill: white;
    text-anchor: middle;
}

3.demo的效果:
这里写图片描述

2018-07-30 15:37:18 u011138190 阅读数 825
  • React服务器端渲染及Next.js实战

    Next.js是一个基于React的一个服务端渲染简约框架。它使用React语法,可以很好的实现代码的模块化,有利于代码的开发和维护。 介绍服务器端渲染原理,深入浅出的讲解nuxt框架的使用。掌握服务器端渲染原理,掌握vue服务器端渲染

    769 人正在学习 去看看 尹涛

集成步骤

官方文档:https://facebook.github.io/react-native/docs/0.54/integration-with-existing-apps
借鉴博客:https://blog.csdn.net/u012455070/article/details/78926526

(1)配置react-native的开发环境
(2)创建一个react-native 的项目
(3)把项目中的android目录里面的东西换成现有的项目
(4)配置android项目的build.gradle文件以及各种依赖
(5)运行项目,运行服务,设置ip端口调试。
(6)各种踩坑问题,比如不支持64位手机的so库问题,找不到服务,由于react native 版本问题导致的各种错误等等

官方文档的集成步骤;
1、安装 node.js python2 jdk 8
这个安装过程就不说了。网上一大堆
2、下载更新React Native CLI
命令:npm install -g react-native-cli
执行这行命令就可以使用react-native 命令了。比如 使用命令运行
3、android 环境配置和模拟器或者手机连接
由于我是做android的,所以这些就跳过了,不清楚的看官方文档或者上网查
4、创建新的应用程序
重点来了,创建应用的时候执行命令:react-native init AwesomeProject 这里的AwesomeProject是项目名称可以随便换,但是必须以字母开头。 由于下载是在国外所以特别慢,所以我们要添加以下国内镜像。

npm config set registry https://registry.npm.taobao.org
npm config set disturl https://npm.taobao.org/dist

这两个都可以,会显著提高下载速度。设置了镜像,然后再执行创建的命令。
5、运行新应用的采坑记录
指定react 和react-native 的版本。
项目目录下的 node_modules 文件夹是 reactnative 所依赖的js的一些东西。
如果没有,我们可以再项目跟目录执行 npm install 命令下载
这个时候我们要注意一下版本号,因为这里遇到坑了。目前最新的react–native可能是0.56 会出现一个bug
比如最新版本

react-native-cli: 2.0.1
react-native: 0.56.0

但是运行的时候出现 Unable to resolve module ‘AccessibilityInfo’ 这个错误,所以建议还是版本低一点,这里使用稳定版
在这里插入图片描述

切换步骤:
(1)先将旧版卸载

npm uninstall -g react-native-cli
npm uninstall -g react-native

(2)再安装指定版本

npm install -g react-native@0.55.4
npm install -g react-native-cli@1.2.0

可以再项目初始化的时候指定下,不指定的话,只要版本对也没问题,自己查看下

react-native init --version="0.55.4" myFirstApp

6、解决红屏错误:Module build failed: Error: Plugin 0 specified in “base” … provided an invalid property of “default”
如果出现上面错误执行下面命令

npm install --save-dev babel-preset-react-native@2.1.0

7、解决 React_Native 无法运行再64位机器上
“/data/data/com.xxx.xxx/lib-main/libgnustl_shared.so” is 32-bit instead of 64-bit 这个错误
参考文章:https://blog.csdn.net/u013531824/article/details/53931307
Android不能同时加载32和64位本机库。 如果您至少有一个依赖库使用ARM64支持编译的扩展,而另外一些依赖库仅支持ARM32,就会出现问题。 系统将检测ARM64依赖关系,加载它,然后拒绝加载仅ARM32的so库,就可能导致应用程序崩溃。
所以,要再项目中excute 64位的几个so库,使用32位的。
这个根据不同项目设置,查一下自己项目用到了哪些64位的so库,都设置一遍
我的项目需要移除这些,然后就不报错了。Native Libs Monitor 这个软件很好用

     ndk {
            //设置支持的SO库架构
            abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
        }

        packagingOptions {
            exclude "lib/arm64-v8a/libgnustl_shared.so"
            exclude "lib/arm64-v8a/libijkffmpeg.so"
            exclude "lib/arm64-v8a/libijkplayer.so"
            exclude "lib/arm64-v8a/libijksdl.so"
            exclude "lib/arm64-v8a/libimagepipeline.so"
            exclude "lib/arm64-v8a/librtmp-jni.so"

        }

8、把android 原生项目拷贝到reactnative 项目的android 目录下
上面步骤可以运行一个react-native 的简单项目,接下来时怎么集成到现有的Android 项目
react-native 项目结构如下:
在这里插入图片描述
我们做android的话,是用Android studio 打开 android 这个目录的。
6、修改gradle 的依赖配置
dependencies {}闭包下 所有的compile 替换位implementation 或者api因为我的Android studio是3.1.3,老报错说compile 2018年底要删除已废弃。另外我的gradle是4.4 build gradle 工具 是3.1.3
(1)Android 项目根目录的 build.gradle文件修改
如下:如果还是不行,建议跟我一样,加上google(). .可能如果是最新版 jencter有问题,用的时候调整下顺序试一试。尽量不要用最新版本的react-native

allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io" }
        //添加这个maven地址,不然无法下载 react-native库
        maven {
            // All of React Native (JS, Android binaries) is installed from npm
            url "$rootDir/../node_modules/react-native/android"
        }
    }
}

再app 的build.gradle 中添加依赖
使用api 或者implementation 都可以, 这里我也制定了版本。

dependencies {
.........
api "com.facebook.react:react-native:0.55.4"
}

7、配置权限

<uses-permission android:name="android.permission.INTERNET" />
//我还要添加一个权限,建议也添加了
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

再调试的时候我们一般需要访问DevSettingsActivity,所以也需要添加到AndroidManifest.xml:
手机摇一摇,或者菜单,设置 电脑服务的ip地址和端口要用到

<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />

8、代码集成
刚刚创建的最简单的react-native 已经有了。我们就再我们Android 项目中加载这个最简单的页面

public class MyReactActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
    private final int OVERLAY_PERMISSION_REQ_CODE = 1;
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //这里不加权限判断 6.0或以上机型会闪退
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                        Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
            }
        }


        mReactRootView = new ReactRootView(this);
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                //这是设置assets目录下的打包过的js文件名 这个文件可用命令生成 但调试期间我们使用npm server动态注入 发布时才将它打进assets
                .setBundleAssetName("index.android.bundle")
                //这里设置js入口文件 旧一点的api可能是setJSMainModuleName 但我的版本是0.51.0 取而代之的是setJSMainModulePath方法
                .setJSMainModulePath("index")
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();

        // 注意这里的“FirstApp”必须
        // 对应index.js”中的“AppRegistry.registerComponent()”的第一个参数值
        // 对应“package.json”中的“name”属性值
        // 最好也将“app.json”中的“name”和“displayName”改成它
        mReactRootView.startReactApplication(mReactInstanceManager, "FirstApp", null);
        setContentView(mReactRootView);

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (!Settings.canDrawOverlays(this)) {

                }
            }
        }
    }
    @Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (mReactInstanceManager != null) {
            mReactInstanceManager.onHostPause(this);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (mReactInstanceManager != null) {
            mReactInstanceManager.onHostResume(this, this);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (mReactInstanceManager != null) {
            mReactInstanceManager.onHostDestroy(this);
        }
        if (mReactRootView != null) {
            mReactRootView.unmountReactApplication();
        }
    }
    @Override
    public void onBackPressed() {
        if (mReactInstanceManager != null) {
            mReactInstanceManager.onBackPressed();
        } else {
            super.onBackPressed();
        }
    }
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
            mReactInstanceManager.showDevOptionsDialog();
            return true;
        }
        return super.onKeyUp(keyCode, event);
    }
}

以上是我的activity,大概意思和官网一样。。代码直接复制就行,需要注意的是:
代码制定的文件名一定要和 配置文件中的一致,要检查
app.json中的name和displayName属性值
ndex.js中registerComponent的第一个参数值
package.json中的name属性值
这几个值要保持一致

9、在真机上运行项目
直接在 reat-native 项目根目录运行命令 react-native run-android , 跳转到上面activity
然后运行起来如果是红屏,说是连不上服务,就摇一摇手机,选择 Debug server host 啥的,设置电脑的ip:8081 这样就能打开了。
或者,直接用Android studio 运行自己的Android 项目, 跳转到上面react 的activity ,也可以是fragment的。
如果出现错误,继续百度谷歌解决。。。有坑是肯定的嘛。先记录这些

2016-05-11 10:35:32 mxy2002924 阅读数 11
  • React服务器端渲染及Next.js实战

    Next.js是一个基于React的一个服务端渲染简约框架。它使用React语法,可以很好的实现代码的模块化,有利于代码的开发和维护。 介绍服务器端渲染原理,深入浅出的讲解nuxt框架的使用。掌握服务器端渲染原理,掌握vue服务器端渲染

    769 人正在学习 去看看 尹涛
  • 官网api文档知识的介绍 http://facebook.github.io/react-native
  • 利用ReactRootView 嵌入js的方式,做到和activity view更细致的嵌入 和 结合。                            http://www.jianshu.com/p/d63c9a22973d    
2017-11-29 12:20:49 weixin_34301132 阅读数 122
  • React服务器端渲染及Next.js实战

    Next.js是一个基于React的一个服务端渲染简约框架。它使用React语法,可以很好的实现代码的模块化,有利于代码的开发和维护。 介绍服务器端渲染原理,深入浅出的讲解nuxt框架的使用。掌握服务器端渲染原理,掌握vue服务器端渲染

    769 人正在学习 去看看 尹涛

前段时间由于性能要求,需把项目d3的版本从v3升级到v4,据了解d3由于在v4版本之前是没有进行模块化的,所以v3代码的扩展性是比较差的,考虑到长远之计,d3在v4版本算是对代码进行了模块化的重构吧,给开发者提供了一些可定制化的东西,所有api变化较大,这个坑还需各种研究文档才能填完,好了,下面开始我的表演了。

初始化force布局

初始化函数从v3的d3.layout.force()变成v4的d3.forceSimulation(),部分参数设置方式如下:

this.force = d3.forceSimulation().alphaDecay(0.1) // 设置alpha衰减系数
                .force("link", d3.forceLink().distance(100)) // distance为连线的距离设置
                .force('collide', d3.forceCollide().radius(() => 30)) // collide 为节点指定一个radius区域来防止节点重叠。
                .force("charge", d3.forceManyBody().strength(-400))  // 节点间的作用力

为布局添加点和线

this.force.nodes(nodes)   // 节点数据
          .force('link', d3.forceLink(links).distance(150));  // 连线数据 distance为连线的距离设置
          .alpha(1);  // 设置alpha值,让里导向图有初始动力
          .restart();   // 启动仿真计时器

由于在v4版本中nodes的xy坐标和加速度vxvy只在nodes中计算一次,所有在变成有节点或连线增加的时候,必须重新执行一次force.nodes(nodes)force('link', d3.forceLink(links)),初始化节点的数据结构。如果在v3版本中,只需在布局初始化时执行即可,在d3会在每次force.start()方法执行时重新初始化一次节点和连线的数据结构,这是一个特别需要注意的地方,另外在v4版本中start方法被遗弃,需使用restart方法。

react部分

将节点的dom结构交给react来控制,方便在节点上添加事件。以下为svg渲染部分代码。

render() {
    const { width, height, nodes, links, scale, translate, selecting, grabbing } = this.props.store;
    return (
      <svg id="svg" ref="svg" width={width} height={height}
        className={cn({
          grab: !selecting && !grabbing,
          grabbing: !selecting && grabbing
        })}
        >
        <g id="outg" ref="outg" transform={`translate(${translate})scale(${scale})`}>
          <g ref="lines" className="lines">
            links.map(link => (
                <line
                  key={`${link.source.uid}_${link.target.uid}`}
                  ref={child => this.links[`${link.source.uid}_${link.target.uid}`] = child}
                  x1={link.source.x}
                  y1={link.source.y}
                  x2={link.target.x}
                  y2={link.target.y}/>
              ))
          </g>
          <g ref="nodes" className="nodes">
            {
              nodes.map(node => (
                <Node key={node.uid}
                  node={node}
                  store={this.props.store}
                  addRef={child => this.nodes[node.uid] = child}/>
              ))
            }
          </g>
        </g>
      </svg>
    );
  }

Node.js 节点
以下为Node Component部分代码

class Node extends Component {
  render() {
    const { node, addRef, store } = this.props;
    const { force } = store;
    return (
      <g className="node"
        ref={child => {
          this._node = child;
          addRef(child);
        }}
        transform={`translate(${node.x || width / 2},${node.y || height / 2})`}
        >
        <g id={node.nodeIndex}>
          // 节点图片dom
        </g>
        {
          node.locked && (
            <Lock
              x={10}
              y={10}
              release={() => {   // 解锁节点
                node.fixed = false;
                node.locked = false;
                node.fx = null;   // 当节点的fx、fy都为null时,节点处于活动状态
                node.fy = null;   
                force.alpha(0.3).restart();  // 释放锁定节点时需设置alpha值并重启计时器,使得布局可以运动。
              }}
              />
          )
        }
      </g>
    );
  }

  componentDidMount() {
    this._node.__data__ = this.props.node;  // 将node节点在d3内部存一份引用,让每次计时器更新的时候自动更改nodes列表中的数据
    d3.select(this._node)  // 各种事件
      .on('click', d => {
          // code
      })
  }
}

Lock.js 节点解除固定按钮。

class Lock extends Component {
  render() {
    const { x, y, fixed } = this.props;
    return (
      <use
        ref="lock"
        xlinkHref="#lock"
        x={x}
        y={y}
        />
    );
  }

  componentDidMount() {
    const { release } = this.props;
    d3.select(this.refs.lock)
      .on('click', () => {
        d3.event.stopPropagation();
        release();
      });
  }
}

仿真计时器 tick

计时器函数,在仿真启动的过程中,计时器的每一帧都会改变一次之前我们在内部存的引用(this._node.__data__ = this.props.node)的node的数据的x值和y值,这时我们需要更新dom结构中的节点和线偏移量。

force.on('tick', () => {
  nodes.forEach(node => {
    if (!node.lock) {
      d3.select(self.nodes[node.uid]).attr('transform', () => `translate(${node.x},${node.y})`);
    }
  });
  links.forEach(link => {
    d3.select(self.links[`${link.source.uid}_${link.target.uid}`])
      .attr('x1', () => link.source.x)
      .attr('y1', () => link.source.y)
      .attr('x2', () => link.target.x)
      .attr('y2', () => link.target.y);
  });
});

在计时器的每一帧中,仿真的alpha系数会不断削减,可通过force.alpha()来获取和设置alpha系数,削减速度由alphaDecay来决定,默认值为0.0228…,衰减系数可通过force.alphaDecay()来获取和设置,当alpha到达一个系数时,仿真将会停止,也就是alpha的目标系数alphaTarget,该值区间为[0,1]. 默认为0,可通过force.alphaTarget()来获取和设置,另外还有一个速度衰减系统velocityDecay ,相当于摩擦力。区间为[0,1], 默认为0.4。在每次tick之后,节点的速度都会等于当前速度乘以1-velocityDecay,和alpha衰减类似,速度衰减越慢最终的效果越好,但是如果速度衰减过慢,可能会导致震荡。以上为tick过程的发生。需要注意的是,在v4版本中,tick事件的callback中不带任何参数,在v3版本的'tick'事件中,我们可通过callback(e)中的e.alpha来获取alpha值,而在v4版本中,alpha值只能通过force.alpha()来获取。

拖拽 Drag

创建拖拽操作

let startTime = 0;
this.drag = d3.drag()
      .on('start', (d) => {
        startTime = (new Date()).getTime();
        d3.event.sourceEvent.stopPropagation();
        if (!d3.event.active) {
           this.force.alphaTarget(0.3).restart();  // 当前alpha值为0,需设置alphaTarget让节点动起来
        }
        d.fx = d.x;
        d.fy = d.y;
      })
      .on('drag', d => {
        this.grabbing = true;
        d.fx = d3.event.x;
        d.fy = d3.event.y;
      })
      .on('end', d => {
        const nowTime = (new Date()).getTime();
        if (!d3.event.active) {
           this.force.alphaTarget(0);  // 让alpha目标值值恢复为默认值0
        }
        if (nowTime - startTime >= 150) {  // 操作150毫秒的拖拽固定节点
          d.fixed = true;
          d.locked = true;
        }
        this.grabbing = false;
      });

将拖拽操作应用到指定的选择集。

    d3.select('#outg').selectAll('.node').call(this.drag);

在内部,拖拽操作通过selection.on来为元素添加监听事件. 事件监听器使用 .drag 来标识这是一个拖拽事件。拖拽drag的v4版本与v3不同的是,v3通过force.drag()创建拖拽操作,拖拽过程事件使用dragstartdragdragend,在拖拽过程中d3内部自动设置alpha相关系数让节点运动起来,而在v4中版本中需要手动设置。

缩放 Zoom

在v4版本中,缩放操作通过transform对象进行,可以通过d3.zoomTransform(selection.node())获取指定节点的缩放状态,也可以通过d3.event.transform来获取当前正在缩放的节点的缩放状态。
与拖拽类似,需要先创建缩放操作。

const self = this;
const outg = d3.select('#outg');
this.zoomObj = d3.zoom()
      .scaleExtent([0.2, 4]) // 缩放范围
      .on('zoom',() => {
        const transform = d3.event.transform;
        self.scale = transform.k;  // 保存当前缩放大小
        self.translate = [transform.x, transform.y];  // 保存当前便宜量
        outg.attr('transform', transform);   // 设置缩放和偏移量 transform对象自带toString()方法
      })
      .on('end', () => {
        // code
      })

将缩放操作应用于选择集,并取消双击操作

const svg = d3.select('#svg');
svg.call(this.zoomObj).on('dblclick.zoom', null);

如果要禁止滚轮滚动缩放,可以在讲zoom事件应用于选择集之后移除zoom事件中的滚轮事件:

svg.call(this.zoomObj).on("wheel.zoom", null);

当缩放事件被调用,d3.event会被设置为当前的zoom事件,zoom event对象由以下几部分组成:

  • target - 当前的缩放zoom behavior。
  • type - 事件类型:“start”, “zoom” 或者 “end”,参考 zoom.on。
  • transform - 当前的zoom transform(缩放变换)。
  • sourceEvent - 原始事件, 比如 mousemove 或 touchmove。

通过按钮缩放、定位视图。

this.zoomObj.transform(d3.select('#svg'), d3.zoomIdentity.translate(newX,newY).scale(newScale))

在v3版本中,可以通过zoom.scale(s)zoom.translate(x, y)设置缩放和偏移量后通过使用'zoom.event(selection)'方法应用到指定选择节点,而在v4中版本需要通过d3.zoomIdentity创建新transform对象,并通过translate(x, y)scale(s)方法设置偏移量和缩放级别,然后将该transform应用到选择集中。另外也可以通过zoom.translateBy(selection, x, y)zoom.translateTo(selection, x, y)zoom.scaleBy(selection, k)zoom.scaleTo(selection, k)方法进行变换。

小结

由于api变动较大,v3升级v4需要耐心看api,查看各个部分的变化,所以,升级需谨慎。最后附上d3.js v4.0中文api


闲适有空一把以上内容封装成组件,详情请见https://github.com/yacan8/d3-react-force

2018-02-05 17:16:50 dKnightL 阅读数 3791
  • React服务器端渲染及Next.js实战

    Next.js是一个基于React的一个服务端渲染简约框架。它使用React语法,可以很好的实现代码的模块化,有利于代码的开发和维护。 介绍服务器端渲染原理,深入浅出的讲解nuxt框架的使用。掌握服务器端渲染原理,掌握vue服务器端渲染

    769 人正在学习 去看看 尹涛

一、参考链接:

 1.官网:https://reacttraining.com/react-router/web/guides/code-splitting/code-splitting-server-rendering

2.某文章:http://blog.csdn.net/mjzhang1993/article/details/79094594

二、源码链接:http://download.csdn.net/download/dknightl/10240961

三、代码

 1.Bundle.js
  Bundle组件会接受一个名为 load 的 props
  load值一是一个组件异步加载的方法 load -> function (cb) {...cb(/* 异步加载的组件 */)},由bundle-loader封装
  这个方法需要传入一个回调函数作为参数
  回调函数会在在方法内异步接收加载完的组件
import React from 'react';

class Bundle extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      // 默认为空
      mod: null
    }
  }


  componentWillMount() {
    // 加载初始状态
    this.load(this.props);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.load !== this.props.load) {
      this.load(nextProps);
    }
  }

  load(props) {
    // 重置状态
    this.setState({
      mod: null
    });
    // 传入组件的组件
    props.load((mod) => {
      this.setState({
        mod: mod.default ? mod.default : mod
      });
    });
  }

  render() {
    // 不为空,则渲染传入的子组件函数
    return this.state.mod ? this.props.children(this.state.mod) : null;
  }
}


export default Bundle;
2.lazyLoad.js
 这个包装函数接受两个值,一个为经过bundle-loader封装的组件,另一个是组件的属性
import React from 'react';
import Bundle from './Bundle';


// 默认加载组件,可以直接返回 null 
const Loading = () => <div>Loading...</div>;


/*
   包装方法,第一次调用后会返回一个组件(函数式组件)
   由于要将其作为路由下的组件,所以需要将 props 传入
      <Bundle load={loadComponent}>
      {Comp => (Comp ? <Comp {...props} /> : <Loading />)}
   </Bundle>
*/ 
const lazyLoad = (loadComponent,props) =>
{//Bundle  包含的是一个函数子组件 由Bundle.js里的this.props.children(this.state.mod)渲染
  return(
   <Bundle load={loadComponent}>
      {Comp => (Comp ? <Comp {...props} /> : <Loading />)}
   </Bundle>
);}


export default lazyLoad;
3.路由使用
有两种使用方式,一种是webpack配置(见下面第四点),
另一种是在组件内直接引用bundle-loader(下面代码没注释的就是)
//import Home from './page/Home.bundle';//这种方式需要配置webpack的loader
//import Detail from './page/Detail.bundle';//这种方式需要配置webpack的loader

//--------------------------------------------------

import Detail from 'bundle-loader?lazy&name=home!./page/Detail.bundle';
import Home from 'bundle-loader?lazy&name=home!./page/Home.bundle';

<BrowserRouter>
  <div>


      <Route exact  path="/" render={()=>   
        <Redirect to="/home"/>               
        // <Home dispatch={dispatch} getState={getState} questionList={value.question}></Home>
      }/>
      <Route path="/home" render={()=>{
        return lazyLoad(Home, {
            dispatch:dispatch,
            getState:getState,
            questionList:value.question
          }
        );
      }}/>                         
      <Route path="/detail" render={(props)=>{
        return lazyLoad(Detail, {
                    pid:props.location.id,
                    questionList:value.question,
                    dispatch:dispatch,
                    answer:value.answer
                }
              );
      }}/>
  </div>
</BrowserRouter> 
4.如果使用webpack配置
注意这段代码要放在js的loader之前,不然可能会报错,这段配合下面这两句引用使用
//import Home from './page/Home.bundle';//这种方式需要配置webpack的loader
//import Detail from './page/Detail.bundle';//这种方式需要配置webpack的loader
       {
                test: /\.bundle\.js$/,
                loader: 'bundle-loader',
                options: {
                    lazy: true,
                    name: '[name]'
                }
            }
完整的webpack.config.js
//__dirname是node.js中的一个全局变量,它指向当前执行脚本所在的目录
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const autoprefixer = require('autoprefixer')

let extractCSS = new ExtractTextPlugin('main.css');

module.exports = {//注意这里是exports不是export
    devtool: 'source-map',//本地调试react
    devServer: {
        contentBase: "./build",//本地服务器所加载的页面所在的目录
        historyApiFallback: true,//不跳转
        port:8888,//设置默认监听端口,如果省略,默认为”8080“
        inline: true//实时刷新
    }, 

    entry: __dirname + "/src/main.js",//唯一入口文件,就像Java中的main方法
    output: {//输出目录
        path: __dirname + "/build",//打包后的js文件存放的地方
        filename: "bundle.js"//打包后的js文件名
    },


    module: {
        //loaders加载器
        rules: [
            {
                test: /\.bundle\.js$/,
                loader: 'bundle-loader',
                include:/page/,
                options: {
                    lazy: true,
                    name: '[name]'
                }
            },{
                test: /\.(js|jsx)$/,//一个匹配loaders所处理的文件的拓展名的正则表达式,这里用来匹配js和jsx文件(必须)
                exclude: /node_modules/,//屏蔽不需要处理的文件(文件夹)(可选)
                use: 'babel-loader'//loader的名称(必须)
            },{ 
                test: /\.(css|less)$/, 
                use:extractCSS.extract({
                        fallback: "style-loader",
                        use: [{
                            loader: "css-loader",
                            options: {
                                modules: true, // 指定启用css modules
                                localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
                            }

                        },{
                            loader: "postcss-loader",
                            options: {
                                plugins: () => [autoprefixer(
                                  { browsers: ['iOS >= 7', 'Android >= 4.1', 
                                     'last 10 Chrome versions', 'last 10 Firefox versions', 
                                     'Safari >= 6', 'ie > 8'] 
                                  }
                                )],
                            },
                        },{
                            loader: "less-loader"
                        }],
                })
            },{
                test: /\.(eot|svg|ttf|woff|woff2|png)\w*/,// font awesome loader
                use: 'file-loader'
            }

        ]
    },
    plugins: [
        extractCSS,
        new HtmlWebpackPlugin({
            template: __dirname + "/src/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
        })
    ]
};





没有更多推荐了,返回首页