react中使用截图组件Cropper组件
--最近项目用react,学习react并使用cropper组件裁剪图片。
(这里开发组件不够统一有用tsx(TypeScript + xml/html)写的组件,有用jsx(javascript+xml/html)写的组件
前言:cropper组件引入到项目中的手顺直接看官方文档;github:https://github.com/fengyuanchen/cropperjs#methods 在线演示url: https://fengyuanchen.github.io/cropper/
1.cropper组件以及各种操作的简单封装。
react-cropper.js文件
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Cropper from 'cropperjs';
const optionProps = [
'dragMode',
'aspectRatio',
'data',
'crop',
// unchangeable props start from here
'viewMode',
'preview',
'responsive',
'restore',
'checkCrossOrigin',
'checkOrientation',
'modal',
'guides',
'center',
'highlight',
'background',
'autoCrop',
'autoCropArea',
'movable',
'rotatable',
'scalable',
'zoomable',
'zoomOnTouch',
'zoomOnWheel',
'wheelZoomRatio',
'cropBoxMovable',
'cropBoxResizable',
'toggleDragModeOnDblclick',
'minContainerWidth',
'minContainerHeight',
'minCanvasWidth',
'minCanvasHeight',
'minCropBoxWidth',
'minCropBoxHeight',
'ready',
'cropstart',
'cropmove',
'cropend',
'zoom',
];
const unchangeableProps = optionProps;
class ReactCropper extends Component {
componentDidMount() {
const options = Object.keys(this.props)
.filter(propKey => optionProps.indexOf(propKey) !== -1)
.reduce((prevOptions, propKey) =>
Object.assign({}, prevOptions, { [propKey]: this.props[propKey] }), {});
this.cropper = new Cropper(this.img, options);
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.src !== this.props.src) {
this.cropper.reset().clear().replace(nextProps.src);
}
if (nextProps.aspectRatio !== this.props.aspectRatio) {
this.setAspectRatio(nextProps.aspectRatio);
}
if (nextProps.data !== this.props.data) {
this.setData(nextProps.data);
}
if (nextProps.dragMode !== this.props.dragMode) {
this.setDragMode(nextProps.dragMode);
}
if (nextProps.cropBoxData !== this.props.cropBoxData) {
this.setCropBoxData(nextProps.cropBoxData);
}
if (nextProps.canvasData !== this.props.canvasData) {
this.setCanvasData(nextProps.canvasData);
}
if (nextProps.moveTo !== this.props.moveTo) {
if (nextProps.moveTo.length > 1) {
this.moveTo(nextProps.moveTo[0], nextProps.moveTo[1]);
} else {
this.moveTo(nextProps.moveTo[0]);
}
}
if (nextProps.zoomTo !== this.props.zoomTo) {
this.zoomTo(nextProps.zoomTo);
}
if (nextProps.rotateTo !== this.props.rotateTo) {
this.rotateTo(nextProps.rotateTo);
}
if (nextProps.scaleX !== this.props.scaleX) {
this.scaleX(nextProps.scaleX);
}
if (nextProps.scaleY !== this.props.scaleY) {
this.scaleY(nextProps.scaleY);
}
if (nextProps.enable !== this.props.enable) {
if (nextProps.enable) {
this.enable();
} else {
this.disable();
}
}
Object.keys(nextProps).forEach((propKey) => {
let isDifferentVal = nextProps[propKey] !== this.props[propKey];
const isUnchangeableProps = unchangeableProps.indexOf(propKey) !== -1;
if (typeof nextProps[propKey] === 'function' && typeof this.props[propKey] === 'function') {
isDifferentVal = nextProps[propKey].toString() !== this.props[propKey].toString();
}
if (isDifferentVal && isUnchangeableProps) {
throw new Error(`prop: ${propKey} can't be change after componentDidMount`);
}
});
}
componentWillUnmount() {
if (this.img) {
// Destroy the cropper, this makes sure events such as resize are cleaned up and do not leak
this.cropper.destroy();
delete this.img;
delete this.cropper;
}
}
setDragMode(mode) {
return this.cropper.setDragMode(mode);
}
setAspectRatio(aspectRatio) {
return this.cropper.setAspectRatio(aspectRatio);
}
getCroppedCanvas(options) {
return this.cropper.getCroppedCanvas(options);
}
setCropBoxData(data) {
return this.cropper.setCropBoxData(data);
}
getCropBoxData() {
return this.cropper.getCropBoxData();
}
setCanvasData(data) {
return this.cropper.setCanvasData(data);
}
getCanvasData() {
return this.cropper.getCanvasData();
}
getImageData() {
return this.cropper.getImageData();
}
getContainerData() {
return this.cropper.getContainerData();
}
setData(data) {
return this.cropper.setData(data);
}
getData(rounded) {
return this.cropper.getData(rounded);
}
crop() {
return this.cropper.crop();
}
move(offsetX, offsetY) {
return this.cropper.move(offsetX, offsetY);
}
moveTo(x, y) {
return this.cropper.moveTo(x, y);
}
zoom(ratio) {
return this.cropper.zoom(ratio);
}
zoomTo(ratio) {
return this.cropper.zoomTo(ratio);
}
rotate(degree) {
return this.cropper.rotate(degree);
}
rotateTo(degree) {
return this.cropper.rotateTo(degree);
}
enable() {
return this.cropper.enable();
}
disable() {
return this.cropper.disable();
}
reset() {
return this.cropper.reset();
}
clear() {
return this.cropper.clear();
}
replace(url, onlyColorChanged) {
return this.cropper.replace(url, onlyColorChanged);
}
scale(scaleX, scaleY) {
return this.cropper.scale(scaleX, scaleY);
}
scaleX(scaleX) {
return this.cropper.scaleX(scaleX);
}
scaleY(scaleY) {
return this.cropper.scaleY(scaleY);
}
render() {
const {
src,
alt,
crossOrigin,
style,
className,
} = this.props;
return (
<div
style={style}
className={className}
>
<img
crossOrigin={crossOrigin}
ref={(img) => { this.img = img; }}
src={src}
alt={alt === undefined ? 'picture' : alt}
style={{ opacity: 0 }}
/>
</div>
);
}
}
ReactCropper.propTypes = {
style: PropTypes.object, // eslint-disable-line react/forbid-prop-types
className: PropTypes.string,
// react cropper options
crossOrigin: PropTypes.string,
src: PropTypes.string,
alt: PropTypes.string,
// props of option can be changed after componentDidmount
aspectRatio: PropTypes.number,
dragMode: PropTypes.oneOf(['crop', 'move', 'none']),
data: PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number,
width: PropTypes.number,
height: PropTypes.number,
rotate: PropTypes.number,
scaleX: PropTypes.number,
scaleY: PropTypes.number,
}),
scaleX: PropTypes.number,
scaleY: PropTypes.number,
enable: PropTypes.bool,
cropBoxData: PropTypes.shape({
left: PropTypes.number,
top: PropTypes.number,
width: PropTypes.number,
height: PropTypes.number,
}),
canvasData: PropTypes.shape({
left: PropTypes.number,
top: PropTypes.number,
width: PropTypes.number,
height: PropTypes.number,
}),
zoomTo: PropTypes.number,
moveTo: PropTypes.arrayOf(PropTypes.number),
rotateTo: PropTypes.number,
// cropperjs options
// https://github.com/fengyuanchen/cropperjs#options
// aspectRatio, dragMode, data
viewMode: PropTypes.oneOf([0, 1, 2, 3]),
preview: PropTypes.string,
responsive: PropTypes.bool,
restore: PropTypes.bool,
checkCrossOrigin: PropTypes.bool,
checkOrientation: PropTypes.bool,
modal: PropTypes.bool,
guides: PropTypes.bool,
center: PropTypes.bool,
highlight: PropTypes.bool,
background: PropTypes.bool,
autoCrop: PropTypes.bool,
autoCropArea: PropTypes.number,
movable: PropTypes.bool,
rotatable: PropTypes.bool,
scalable: PropTypes.bool,
zoomable: PropTypes.bool,
zoomOnTouch: PropTypes.bool,
zoomOnWheel: PropTypes.bool,
wheelZoomRatio: PropTypes.number,
cropBoxMovable: PropTypes.bool,
cropBoxResizable: PropTypes.bool,
toggleDragModeOnDblclick: PropTypes.bool,
minContainerWidth: PropTypes.number,
minContainerHeight: PropTypes.number,
minCanvasWidth: PropTypes.number,
minCanvasHeight: PropTypes.number,
minCropBoxWidth: PropTypes.number,
minCropBoxHeight: PropTypes.number,
ready: PropTypes.func,
cropstart: PropTypes.func,
cropmove: PropTypes.func,
cropend: PropTypes.func,
crop: PropTypes.func,
zoom: PropTypes.func,
};
ReactCropper.defaultProps = {
src: null,
dragMode: 'crop',
data: null,
scaleX: 1,
scaleY: 1,
enable: true,
zoomTo: 1,
rotateTo: 0,
};
export default ReactCropper;
2.cropper组件调用的简单封装
CropperView.jsx文件
import React, { Component, useEffect } from 'react';
import $ from "jquery";
import Cropper from './cropper/react-cropper'
import 'cropperjs/dist/cropper.css'
/* global FileReader */
var showCropArea = true;
export default class CropperView extends Component {
constructor(props) {
super(props);
this.state = {
cropResult: null,
};
// this.cropper = this;
this.onChange = this.onChange.bind(this);
this.src = props.src;
}
// componentDidMount(){
// useEffect(() => {
// alert("cropZone" + this.cropper);
// // if (typeof this.cropper.getCroppedCanvas() === 'undefined') {
// // return;
// // }
// alert("left:" + this.cropper.getCropBoxData().left
// + "top:" + this.cropper.getCropBoxData().top
// + "width:" + this.cropper.getCropBoxData().width
// + "height:" + this.cropper.getCropBoxData().height);
// }, [this.props.save]);
// }
onChange(e) {
e.preventDefault();
let files;
if (e.dataTransfer) {
files = e.dataTransfer.files;
} else if (e.target) {
files = e.target.files;
}
const reader = new FileReader();
reader.onload = () => {
this.setState({ src: reader.result });
};
reader.readAsDataURL(files[0]);
}
cropZone() {
if (typeof this.cropper.getCroppedCanvas() === 'undefined') {
return;
}
alert("left:" + this.cropper.getCropBoxData().left
+ "top:" + this.cropper.getCropBoxData().top
+ "width:" + this.cropper.getCropBoxData().width
+ "height:" + this.cropper.getCropBoxData().height);
$(".cropper-crop-box").hide();
$(".cropper-drag-box").hide();
$(".cropper-wrap-box").append(
"<div style=\"width:" + this.cropper.getCropBoxData().width + ";"
+ "height:" + this.cropper.getCropBoxData().height + ";"
+ "background:#0000FF; opacity:0.3"
+ "position:absolute; left:" + this.cropper.getCropBoxData().left +";"
+ "top:" + this.cropper.getCropBoxData().top + ";\">");
}
showCropZone() {
if (showCropArea) {
$(".cropper-crop-box").css("display", "block");
$(".cropper-drag-box").css("display", "block");
} else {
$(".cropper-crop-box").hide();
$(".cropper-drag-box").hide();
}
}
creatCrop(){
this.cropper.crop();
}
clearCrop(){
this.cropper.clear();
}
resetCrop(){
this.cropper.reset();
}
moveLeft(){
this.cropper.move(-5, 0);
}
moveRight(){
this.cropper.move(5, 0);
}
moveUp(){
console.log("====moveUp===");
try {
this.cropper.move(0, -5);
}catch (err){
console.log(err);
}
}
moveDown(){
this.cropper.move(0, 5);
}
enlarge(){
try {
// 放大
// this.cropper.zoom(0.1);
var allCanvasDate = this.cropper.getCanvasData();
var newCanvasDate = {left:allCanvasDate.left, top: allCanvasDate.top,
width: allCanvasDate.width*2, height: allCanvasDate.height*2}
this.cropper.setCanvasData(newCanvasDate);
}catch (err){
console.log(err);
}
}
shrink(){
try {
// 缩小
// this.cropper.zoom(-0.1)
var allCanvasDate = this.cropper.getCanvasData();
var newCanvasDate = {left:allCanvasDate.left, top: allCanvasDate.top,
width: allCanvasDate.width*0.5, height: allCanvasDate.height*0.5}
this.cropper.setCanvasData(newCanvasDate);
}catch (err){
console.log(err);
}
}
test(){
try {
var allDate = this.cropper.getData(true);
alert(allDate.toString());
var par = {x:allDate.x, y:allDate.y, width:allDate.width*2, height:allDate.height*2,
rotate:allDate.rotate, scaleX:allDate.scaleX, scaleY:allDate.scaleY}
this.cropper.setData(par);
}catch (err){
console.log(err);
}
}
moveCrop(){
//this.cropper.movecrop();
console.log("===move===crop===");
}
reduceCrop(){
var par = {left:this.cropper.getCropBoxData().left, top:this.cropper.getCropBoxData().top,
width:this.cropper.getCropBoxData().width*0.8, height:this.cropper.getCropBoxData().height*0.8}
this.cropper.setCropBoxData(par);
}
raiseCrop(){
var par = {left:this.cropper.getCropBoxData().left, top:this.cropper.getCropBoxData().top,
width:this.cropper.getCropBoxData().width*1.2, height:this.cropper.getCropBoxData().height*1.2}
this.cropper.setCropBoxData(par);
}
CropLeft(){
var par = {left:this.cropper.getCropBoxData().left - 10, top:this.cropper.getCropBoxData().top,
width:this.cropper.getCropBoxData().width, height:this.cropper.getCropBoxData().height}
this.cropper.setCropBoxData(par);
}
CropRight(){
var par = {left:this.cropper.getCropBoxData().left + 10, top:this.cropper.getCropBoxData().top,
width:this.cropper.getCropBoxData().width, height:this.cropper.getCropBoxData().height}
this.cropper.setCropBoxData(par);
}
CropUp(){
var par = {left:this.cropper.getCropBoxData().left, top:this.cropper.getCropBoxData().top - 10,
width:this.cropper.getCropBoxData().width, height:this.cropper.getCropBoxData().height}
this.cropper.setCropBoxData(par);
}
CropDown(){
var par = {left:this.cropper.getCropBoxData().left, top:this.cropper.getCropBoxData().top + 10,
width:this.cropper.getCropBoxData().width, height:this.cropper.getCropBoxData().height}
this.cropper.setCropBoxData(par);
}
render() {
return (
<div className="r-view" style={{ position:'absolute', left:'185px', top:"83px" }}>
<Cropper
style={{ height:'100%', width:'auto' }}
aspectRatio={16 / 9}
preview=".img-preview"
guides={false}
src={this.props.src}
viewMode={2}
minContainerWidth={585}
minContainerHeight={430}
ref={cropper => { this.cropper = cropper; }}
zoomable={true}
zoomOnTouch={true}
/>
</div>
);
}
}
3.cropper组件与各种按钮操作的绑定(原因:设备上不会支持手指在选择区域的操作以及图片的放大缩小操作),页面整合组件。
CropperScreen.tsx文件
import * as React from 'react';
import styled from "styled-components";
import useTranslate from "../../hooks/useTranslate";
import CropView from "./CropperView";
import 'cropperjs/dist/cropper.css' import FormView from "./FormView" const AppStyle = styled.div`
background: #CCC;
`; var showCropArea = false; export default function CropperScreen() {
const t = useTranslate(); const handleBackClicked = () => {
$("#viewable").show();
$("#scan_settings_id").css("display","none");
}; const doCropClicked = () => {
// showCropArea = !showCropArea;
// if (showCropArea) {
// alert("disabled false");
// $("#btn_save_crop").removeAttr("disabled");
// } else {
// alert("disabled true");
// $("#btn_save_crop").attr("disabled", "true");
// }
// cropUser.showCropZone(showCropArea); cropUser.clearCrop(); }; const doSaveCropClicked = () => {
cropUser.cropZone();
}; const handReset = () => {
cropUser.resetCrop();
cropUser.creatCrop();
} const handMoveLeft = () => {
cropUser.moveLeft();
} const handMoveRight = () => {
cropUser.moveRight();
} const handMoveUp = () => {
console.log("====handMoveUp===");
try {
cropUser.moveUp();
}catch (err){
console.log(err);
}
} const handMoveDown = () => {
cropUser.moveDown();
} const handMoveCrop = () => {
alert("unknow");
cropUser.moveCrop();
} const handReduceCrop = () => {
cropUser.reduceCrop();
} const handRaiseCrop = () => {
cropUser.raiseCrop()
} const handEnlarge = () => {
cropUser.enlarge();
} const handShrink = () => {
cropUser.shrink();
} const handCropLeft = () => {
cropUser.CropLeft()
} const handCropRight = () => {
cropUser.CropRight()
} const handCropUp = () => {
cropUser.CropUp()
} const handCropDown = () => {
cropUser.CropDown()
} const handTest = () => {
cropUser.test()
} const handleSubmit = () => {
alert("====handleSubmit====");
console.log(formUser); console.log(formUser.state);
} let maxItem = 2;
let cropUser
let formUser
var initValue = {name:'tom',job: '12'} return (
<AppStyle id="scan_settings_id" className="r-view" style={{display:"none", height:"470px"}}>
<header className="r-titlebar">
<a href="#" className="r-titlebar__back" onClick={handleBackClicked} />
<h1 className="r-titlebar__title">Preview</h1>
</header>
<div style={{position:"absolute", left:35}}>
</div>
<CropView ref={cropView => {cropUser = cropView}} src={require('../smartsdk-css/img/test.png')}/>
<div className="r-floating-island">
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={doCropClicked}>Crop</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={doSaveCropClicked}>Save</button>
</div>
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handReset}>reset</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveCrop}>mcrop</button>
</div>
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveLeft}>left</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveRight}>right</button>
</div>
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveUp}>up</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveDown}>down</button>
</div>
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handEnlarge}>+</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handShrink}>-</button>
</div>
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handReduceCrop}>reduce</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handRaiseCrop}>raise</button>
</div> <div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handCropLeft}>cropr</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handCropRight}>cropl</button>
</div> <div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handCropUp}>cropu</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handCropDown}>cropd</button>
</div>
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handTest}>test</button>
</div> {/* <FormView handleSubmit={handleSubmit} ref={formView => {formUser = formView}} props = {initValue}/> */} {/* <button className="r-start-button">
{t("dapi:cba.common.start")}
</button> */}
</div>
</AppStyle >
);
}
4.最后调用整体的组件,页面展示

页面

react中使用截图组件Cropper组件的更多相关文章
- React中的Context——从父组件传递数据
简介:在React中,数据可以以流的形式自上而下的传递,每当你使用一个组件的时候,你可以看到组件的props属性会自上而下的传递.但是,在某些情况下,我们不想通过父组件的props属性一级一级的往下传 ...
- react 中的无状态函数式组件
无状态函数式组件,顾名思义,无状态,也就是你无法使用State,也无法使用组件的生命周期方法,这就决定了函数组件都是展示性组件,接收Props,渲染DOM,而不关注其他逻辑. 其实无状态函数式组件也是 ...
- 基于 React 实现一个 Transition 过渡动画组件
过渡动画使 UI 更富有表现力并且易于使用.如何使用 React 快速的实现一个 Transition 过渡动画组件? 基本实现 实现一个基础的 CSS 过渡动画组件,通过切换 CSS 样式实现简单的 ...
- 理解React中es6方法创建组件的this
首发于:https://mingjiezhang.github.io/(转载请说明此出处). 在JavaScript中,this对象是运行时基于函数的执行环境(也就是上下文)绑定的. 从react中的 ...
- React中父组件与子组件之间的数据传递和标准化的思考
React中父组件与子组件之间的数据传递的的实现大家都可以轻易做到,但对比很多人的实现方法,总是会有或多或少的差异.在一个团队中,这种实现的差异体现了每个人各自的理解的不同,但是反过来思考,一个团队用 ...
- React 深入系列1:React 中的元素、组件、实例和节点
文:徐超,<React进阶之路>作者 授权发布,转载请注明作者及出处 React 深入系列,深入讲解了React中的重点概念.特性和模式等,旨在帮助大家加深对React的理解,以及在项目中 ...
- [转] React 中组件间通信的几种方式
在使用 React 的过程中,不可避免的需要组件间进行消息传递(通信),组件间通信大体有下面几种情况: 父组件向子组件通信 子组件向父组件通信 跨级组件之间通信 非嵌套组件间通信 下面依次说下这几种通 ...
- react中直接调用子组件的方法(非props方式)
我们都知道在 react中,若要在父组件调用子组件的方法,通常我们会采用在父组件定义一个方法,作为props转给子组件,然后执行该方法,可以获取到子组件传回的参数以得到我们的目的. 显而易见,这个执行 ...
- React 中的 Component、PureComponent、无状态组件 之间的比较
React 中的 Component.PureComponent.无状态组件之间的比较 table th:first-of-type { width: 150px; } 组件类型 说明 React.c ...
随机推荐
- c语言数据结构,你可能还不知道的顺序表
数据结构顺序表 顺序表定义 1,前言 线性表的顺序存储又称为顺序表.它是用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻.其最大的特点就是:元素的逻辑 ...
- CentOS7编译安装升级openssh8.7p1
因生成环境服务器安全扫描出的漏洞问题,只能升级最新的openssh,适用于centos6和centos7的升级使用. 一.编译前工作 openssl版本要求1.0.1以上,zlib版本要求1.1.4以 ...
- Numpy的各种下标操作
技术背景 本文所使用的Numpy版本为:Version: 1.20.3.基于Python和C++开发的Numpy一般被认为是Python中最好的Matlab替代品,其中最常见的就是各种Numpy矩阵类 ...
- python3发微信脚本
企业微信发微信脚本 #!/usr/bin/env python # -*- coding: utf-8 -*- #GuoYabin import requests,json,sys,imp imp.r ...
- python数据分析与挖掘实战第二版pdf-------详细代码与实现
[书名]:PYTHON数据分析与挖掘实战 第2版[作者]:张良均,谭立云,刘名军,江建明著[出版社]:北京:机械工业出版社[时间]:2020[页数]:340[isbn]:9787111640028 学 ...
- 万能BI工具时代,聊天记录也能做数据分析?
最近知乎上有个问题火了: 看了高赞的一些答案,最大的感受就是:婚前"泰国.新加坡.印度尼西亚"婚后"玩具.幼儿园.全部都是娃".作为一个适龄青年,也突然对自己结 ...
- SUSCTF2022 Misc-AUDIO&RA2
前言:这次参加了susctf感受颇深,题目难度不是很大很大,但是很考验基础的技术熟练度,比如re这次就因为不会套脚本去解密,导致卡死在了第一道题,一道没做出来.所以只做了做misc和web. RA2 ...
- linux下确认裸盘是否使用-渐入佳境
--作者:飞翔的小胖猪 --创建时间:2021年3月9日 6.1 概述 在私有云和虚拟化环境中业务方经常会根据自己的业务情况申请磁盘用作数据存储用.如果申请了磁盘但没有使用的情况,将极大的造成资源的浪 ...
- omnet++:官方文档翻译总结(五)
Part 6 - 用IDE将结果可视化 学习翻译自:Visualizing the Results - OMNeT++ Technical Articles ①将输出的数值和向量数据可视化(用tict ...
- 资源管理模式:Evictor模式
Evictor模式描述了何时以及如何释放资源以优化资源管理.这个模式让我们可以配置不同的策略来自动决定哪些资源应该释放,以及应该在什么时候释放. 实例 考虑一个网络管理系统--管理多个网络元素.这些网 ...